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 /** The {@link ContentView} showing the current page or {@code null} if the tab is frozen. */
103 private ContentView mContentView;
105 /** InfoBar container to show InfoBars for this tab. */
106 private InfoBarContainer mInfoBarContainer;
108 /** Manages app banners shown for this tab. */
109 private AppBannerManager mAppBannerManager;
111 /** The sync id of the Tab if session sync is enabled. */
115 * The {@link ContentViewCore} for the current page, provided for convenience. This always
116 * equals {@link ContentView#getContentViewCore()}, or {@code null} if mContentView is
119 private ContentViewCore mContentViewCore;
122 * A list of Tab observers. These are used to broadcast Tab events to listeners.
124 private final ObserverList<TabObserver> mObservers = new ObserverList<TabObserver>();
126 // Content layer Observers and Delegates
127 private ContentViewClient mContentViewClient;
128 private WebContentsObserverAndroid mWebContentsObserver;
129 private VoiceSearchTabHelper mVoiceSearchTabHelper;
130 private TabChromeWebContentsDelegateAndroid mWebContentsDelegate;
131 private DomDistillerFeedbackReporter mDomDistillerFeedbackReporter;
134 * If this tab was opened from another tab, store the id of the tab that
135 * caused it to be opened so that we can activate it when this tab gets
138 private int mParentId = INVALID_TAB_ID;
141 * Whether the tab should be grouped with its parent tab.
143 private boolean mGroupedWithParent = true;
145 private boolean mIsClosing = false;
148 * A default {@link ChromeContextMenuItemDelegate} that supports some of the context menu
151 protected class TabChromeContextMenuItemDelegate
152 extends EmptyChromeContextMenuItemDelegate {
153 private final Clipboard mClipboard;
156 * Builds a {@link TabChromeContextMenuItemDelegate} instance.
158 public TabChromeContextMenuItemDelegate() {
159 mClipboard = new Clipboard(getApplicationContext());
163 public boolean isIncognito() {
168 public void onSaveToClipboard(String text, boolean isUrl) {
169 mClipboard.setText(text, text);
173 public void onSaveImageToClipboard(String url) {
174 mClipboard.setHTMLText("<img src=\"" + url + "\">", url, url);
179 * A basic {@link ChromeWebContentsDelegateAndroid} that forwards some calls to the registered
180 * {@link TabObserver}s. Meant to be overridden by subclasses.
182 public class TabChromeWebContentsDelegateAndroid
183 extends ChromeWebContentsDelegateAndroid {
185 public void onLoadProgressChanged(int progress) {
186 for (TabObserver observer : mObservers) {
187 observer.onLoadProgressChanged(Tab.this, progress);
192 public void onLoadStarted() {
193 for (TabObserver observer : mObservers) observer.onLoadStarted(Tab.this);
197 public void onLoadStopped() {
198 for (TabObserver observer : mObservers) observer.onLoadStopped(Tab.this);
202 public void onUpdateUrl(String url) {
203 for (TabObserver observer : mObservers) observer.onUpdateUrl(Tab.this, url);
207 public void showRepostFormWarningDialog(final ContentViewCore contentViewCore) {
208 RepostFormWarningDialog warningDialog = new RepostFormWarningDialog(
212 contentViewCore.cancelPendingReload();
217 contentViewCore.continuePendingReload();
220 Activity activity = (Activity) mContext;
221 warningDialog.show(activity.getFragmentManager(), null);
225 public void toggleFullscreenModeForTab(boolean enableFullscreen) {
226 for (TabObserver observer : mObservers) {
227 observer.onToggleFullscreenMode(Tab.this, enableFullscreen);
232 public void navigationStateChanged(int flags) {
233 if ((flags & INVALIDATE_TYPE_TITLE) != 0) {
234 for (TabObserver observer : mObservers) observer.onTitleUpdated(Tab.this);
236 if ((flags & INVALIDATE_TYPE_URL) != 0) {
237 for (TabObserver observer : mObservers) observer.onUrlUpdated(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);
287 * Creates an instance of a {@link Tab} with no id.
288 * @param incognito Whether or not this tab is incognito.
289 * @param context An instance of a {@link Context}.
290 * @param window An instance of a {@link WindowAndroid}.
292 public Tab(boolean incognito, Context context, WindowAndroid window) {
293 this(INVALID_TAB_ID, incognito, context, window);
297 * Creates an instance of a {@link Tab}.
298 * @param id The id this tab should be identified with.
299 * @param incognito Whether or not this tab is incognito.
300 * @param context An instance of a {@link Context}.
301 * @param window An instance of a {@link WindowAndroid}.
303 public Tab(int id, boolean incognito, Context context, WindowAndroid window) {
304 this(INVALID_TAB_ID, id, incognito, context, window);
308 * Creates an instance of a {@link Tab}.
309 * @param id The id this tab should be identified with.
310 * @param parentId The id id of the tab that caused this tab to be opened.
311 * @param incognito Whether or not this tab is incognito.
312 * @param context An instance of a {@link Context}.
313 * @param window An instance of a {@link WindowAndroid}.
315 public Tab(int id, int parentId, boolean incognito, Context context, WindowAndroid window) {
316 // We need a valid Activity Context to build the ContentView with.
317 assert context == null || context instanceof Activity;
319 mId = generateValidId(id);
320 mParentId = parentId;
321 mIncognito = incognito;
322 // TODO(dtrainor): Only store application context here.
324 mApplicationContext = context != null ? context.getApplicationContext() : null;
325 mWindowAndroid = window;
329 * Adds a {@link TabObserver} to be notified on {@link Tab} changes.
330 * @param observer The {@link TabObserver} to add.
332 public final void addObserver(TabObserver observer) {
333 mObservers.addObserver(observer);
337 * Removes a {@link TabObserver}.
338 * @param observer The {@link TabObserver} to remove.
340 public final void removeObserver(TabObserver observer) {
341 mObservers.removeObserver(observer);
345 * @return Whether or not this tab has a previous navigation entry.
347 public boolean canGoBack() {
348 return mContentViewCore != null && mContentViewCore.canGoBack();
352 * @return Whether or not this tab has a navigation entry after the current one.
354 public boolean canGoForward() {
355 return mContentViewCore != null && mContentViewCore.canGoForward();
359 * Goes to the navigation entry before the current one.
361 public void goBack() {
362 if (mContentViewCore != null) mContentViewCore.goBack();
366 * Goes to the navigation entry after the current one.
368 public void goForward() {
369 if (mContentViewCore != null) mContentViewCore.goForward();
373 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
374 if (mContentViewCore != null) {
375 return mContentViewCore.getDirectedNavigationHistory(isForward, itemLimit);
377 return new NavigationHistory();
382 public void goToNavigationIndex(int index) {
383 if (mContentViewCore != null) mContentViewCore.goToNavigationIndex(index);
387 * Loads the current navigation if there is a pending lazy load (after tab restore).
389 public void loadIfNecessary() {
390 if (mContentViewCore != null) mContentViewCore.loadIfNecessary();
394 * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
396 protected void requestRestoreLoad() {
397 if (mContentViewCore != null) mContentViewCore.requestRestoreLoad();
401 * Causes this tab to navigate to the specified URL.
402 * @param params parameters describing the url load. Note that it is important to set correct
403 * page transition as it is used for ranking URLs in the history so the omnibox
404 * can report suggestions correctly.
405 * @return FULL_PRERENDERED_PAGE_LOAD or PARTIAL_PRERENDERED_PAGE_LOAD if the page has been
406 * prerendered. DEFAULT_PAGE_LOAD if it had not.
408 public int loadUrl(LoadUrlParams params) {
411 // We load the URL from the tab rather than directly from the ContentView so the tab has a
412 // chance of using a prerenderer page is any.
413 int loadType = nativeLoadUrl(
416 params.getVerbatimHeaders(),
417 params.getPostData(),
418 params.getTransitionType(),
419 params.getReferrer() != null ? params.getReferrer().getUrl() : null,
420 // Policy will be ignored for null referrer url, 0 is just a placeholder.
421 // TODO(ppi): Should we pass Referrer jobject and add JNI methods to read it from
423 params.getReferrer() != null ? params.getReferrer().getPolicy() : 0);
427 for (TabObserver observer : mObservers) {
428 observer.onLoadUrl(this, params.getUrl(), loadType);
434 * @return Whether or not the {@link Tab} is currently showing an interstitial page, such as
437 public boolean isShowingInterstitialPage() {
438 ContentViewCore contentViewCore = getContentViewCore();
439 return contentViewCore != null && contentViewCore.isShowingInterstitialPage();
443 * @return Whether or not the tab has something valid to render.
445 public boolean isReady() {
446 return mNativePage != null || (mContentViewCore != null && mContentViewCore.isReady());
450 * @return The {@link View} displaying the current page in the tab. This might be a
451 * {@link ContentView} but could potentially be any instance of {@link View}. This can
452 * be {@code null}, if the tab is frozen or being initialized or destroyed.
454 public View getView() {
455 return mNativePage != null ? mNativePage.getView() : mContentView;
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.
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.
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 if (mNativePage != null) return mNativePage.getBackgroundColor();
537 if (mContentViewCore != null) return mContentViewCore.getBackgroundColor();
542 * @return The web contents associated with this tab.
544 public WebContents getWebContents() {
545 if (mNativeTabAndroid == 0) return null;
546 return nativeGetWebContents(mNativeTabAndroid);
550 * @return The profile associated with this tab.
552 public Profile getProfile() {
553 if (mNativeTabAndroid == 0) return null;
554 return nativeGetProfileAndroid(mNativeTabAndroid);
558 * For more information about the uniqueness of {@link #getId()} see comments on {@link Tab}.
560 * @return The id representing this tab.
568 * @return Whether or not this tab is incognito.
570 public boolean isIncognito() {
575 * @return The {@link ContentView} associated with the current page, or {@code null} if
576 * there is no current page or the current page is displayed using something besides a
577 * {@link ContentView}.
579 public ContentView getContentView() {
580 return mNativePage == null ? mContentView : null;
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 something besides a
586 * {@link ContentView}.
588 public ContentViewCore getContentViewCore() {
589 return mNativePage == null ? mContentViewCore : null;
593 * @return The {@link NativePage} associated with the current page, or {@code null} if there is
594 * no current page or the current page is displayed using something besides
595 * {@link NativePage}.
597 public NativePage getNativePage() {
602 * @return Whether or not the {@link Tab} represents a {@link NativePage}.
604 public boolean isNativePage() {
605 return mNativePage != null;
609 * Set whether or not the {@link ContentViewCore} should be using a desktop user agent for the
610 * currently loaded page.
611 * @param useDesktop If {@code true}, use a desktop user agent. Otherwise use a mobile one.
612 * @param reloadOnChange Reload the page if the user agent has changed.
614 public void setUseDesktopUserAgent(boolean useDesktop, boolean reloadOnChange) {
615 if (mContentViewCore != null) {
616 mContentViewCore.setUseDesktopUserAgent(useDesktop, reloadOnChange);
621 * @return Whether or not the {@link ContentViewCore} is using a desktop user agent.
623 public boolean getUseDesktopUserAgent() {
624 return mContentViewCore != null && mContentViewCore.getUseDesktopUserAgent();
628 * @return The current {ToolbarModelSecurityLevel} for the tab.
630 public int getSecurityLevel() {
631 if (mNativeTabAndroid == 0) return ToolbarModelSecurityLevel.NONE;
632 return nativeGetSecurityLevel(mNativeTabAndroid);
636 * @return The sync id of the tab if session sync is enabled, {@code 0} otherwise.
639 protected int getSyncId() {
644 * @param syncId The sync id of the tab if session sync is enabled.
647 protected void setSyncId(int syncId) {
652 * @return An {@link ObserverList.RewindableIterator} instance that points to all of
653 * the current {@link TabObserver}s on this class. Note that calling
654 * {@link java.util.Iterator#remove()} will throw an
655 * {@link UnsupportedOperationException}.
657 protected ObserverList.RewindableIterator<TabObserver> getTabObservers() {
658 return mObservers.rewindableIterator();
662 * @return The {@link ContentViewClient} currently bound to any {@link ContentViewCore}
663 * associated with the current page. There can still be a {@link ContentViewClient}
664 * even when there is no {@link ContentViewCore}.
666 protected ContentViewClient getContentViewClient() {
667 return mContentViewClient;
671 * @param client The {@link ContentViewClient} to be bound to any current or new
672 * {@link ContentViewCore}s associated with this {@link Tab}.
674 protected void setContentViewClient(ContentViewClient client) {
675 if (mContentViewClient == client) return;
677 ContentViewClient oldClient = mContentViewClient;
678 mContentViewClient = client;
680 if (mContentViewCore == null) return;
682 if (mContentViewClient != null) {
683 mContentViewCore.setContentViewClient(mContentViewClient);
684 } else if (oldClient != null) {
685 // We can't set a null client, but we should clear references to the last one.
686 mContentViewCore.setContentViewClient(new ContentViewClient());
691 * Triggers the showing logic for the view backing this tab.
693 protected void show() {
694 if (mContentViewCore != null) mContentViewCore.onShow();
698 * Triggers the hiding logic for the view backing the tab.
700 protected void hide() {
701 if (mContentViewCore != null) mContentViewCore.onHide();
705 * Shows the given {@code nativePage} if it's not already showing.
706 * @param nativePage The {@link NativePage} to show.
708 protected void showNativePage(NativePage nativePage) {
709 if (mNativePage == nativePage) return;
710 NativePage previousNativePage = mNativePage;
711 mNativePage = nativePage;
712 pushNativePageStateToNavigationEntry();
713 for (TabObserver observer : mObservers) observer.onContentChanged(this);
714 destroyNativePageInternal(previousNativePage);
718 * Hides the current {@link NativePage}, if any, and shows the {@link ContentView}.
720 protected void showRenderedPage() {
721 if (mNativePage == null) return;
722 NativePage previousNativePage = mNativePage;
724 for (TabObserver observer : mObservers) observer.onContentChanged(this);
725 destroyNativePageInternal(previousNativePage);
729 * Initializes this {@link Tab}.
731 public void initialize() {
736 * Builds the native counterpart to this class. Meant to be overridden by subclasses to build
737 * subclass native counterparts instead. Subclasses should not call this via super and instead
738 * rely on the native class to create the JNI association.
740 protected void initializeNative() {
741 if (mNativeTabAndroid == 0) nativeInit();
742 assert mNativeTabAndroid != 0;
746 * A helper method to initialize a {@link ContentView} without any native WebContents pointer.
748 protected final void initContentView() {
749 initContentView(ContentViewUtil.createNativeWebContents(mIncognito));
753 * Creates and initializes the {@link ContentView}.
755 * @param nativeWebContents The native web contents pointer.
757 protected void initContentView(long nativeWebContents) {
758 setContentView(ContentView.newInstance(mContext, nativeWebContents, getWindowAndroid()));
762 * Completes the {@link ContentView} specific initialization around a native WebContents
763 * pointer. {@link #getNativePage()} will still return the {@link NativePage} if there is one.
764 * All initialization that needs to reoccur after a web contents swap should be added here.
766 * NOTE: If you attempt to pass a native WebContents that does not have the same incognito
767 * state as this tab this call will fail.
769 * @param view The content view that needs to be set as active view for the tab.
771 protected void setContentView(ContentView view) {
772 NativePage previousNativePage = mNativePage;
774 destroyNativePageInternal(previousNativePage);
778 mContentViewCore = mContentView.getContentViewCore();
779 mWebContentsDelegate = createWebContentsDelegate();
780 mWebContentsObserver = new TabWebContentsObserverAndroid(mContentViewCore);
781 mVoiceSearchTabHelper = new VoiceSearchTabHelper(mContentViewCore);
783 if (mContentViewClient != null) mContentViewCore.setContentViewClient(mContentViewClient);
785 assert mNativeTabAndroid != 0;
786 nativeInitWebContents(
787 mNativeTabAndroid, mIncognito, mContentViewCore, mWebContentsDelegate,
788 new TabContextMenuPopulator(createContextMenuPopulator()));
790 // In the case where restoring a Tab or showing a prerendered one we already have a
791 // valid infobar container, no need to recreate one.
792 if (mInfoBarContainer == null) {
793 // The InfoBarContainer needs to be created after the ContentView has been natively
795 WebContents webContents = view.getContentViewCore().getWebContents();
796 mInfoBarContainer = new InfoBarContainer(
797 (Activity) mContext, createAutoLoginProcessor(), getId(), getContentView(),
800 mInfoBarContainer.onParentViewChanged(getId(), getContentView());
803 if (AppBannerManager.isEnabled() && mAppBannerManager == null) {
804 mAppBannerManager = new AppBannerManager(this);
807 if (DomDistillerFeedbackReporter.isEnabled() && mDomDistillerFeedbackReporter == null) {
808 mDomDistillerFeedbackReporter = new DomDistillerFeedbackReporter(this);
811 for (TabObserver observer : mObservers) observer.onContentChanged(this);
815 * Cleans up all internal state, destroying any {@link NativePage} or {@link ContentView}
816 * currently associated with this {@link Tab}. This also destroys the native counterpart
817 * to this class, which means that all subclasses should erase their native pointers after
818 * this method is called. Once this call is made this {@link Tab} should no longer be used.
820 public void destroy() {
821 for (TabObserver observer : mObservers) observer.onDestroyed(this);
824 NativePage currentNativePage = mNativePage;
826 destroyNativePageInternal(currentNativePage);
827 destroyContentView(true);
829 // Destroys the native tab after destroying the ContentView but before destroying the
830 // InfoBarContainer. The native tab should be destroyed before the infobar container as
831 // destroying the native tab cleanups up any remaining infobars. The infobar container
832 // expects all infobars to be cleaned up before its own destruction.
833 assert mNativeTabAndroid != 0;
834 nativeDestroy(mNativeTabAndroid);
835 assert mNativeTabAndroid == 0;
837 if (mInfoBarContainer != null) {
838 mInfoBarContainer.destroy();
839 mInfoBarContainer = null;
844 * @return Whether or not this Tab has a live native component.
846 public boolean isInitialized() {
847 return mNativeTabAndroid != 0;
851 * @return The url associated with the tab.
854 public String getUrl() {
855 return mContentViewCore != null ? mContentViewCore.getUrl() : "";
859 * @return The tab title.
862 public String getTitle() {
863 if (mNativePage != null) return mNativePage.getTitle();
864 if (mContentViewCore != null) return mContentViewCore.getTitle();
869 * @return The bitmap of the favicon scaled to 16x16dp. null if no favicon
870 * is specified or it requires the default favicon.
871 * TODO(bauerb): Upstream implementation.
873 public Bitmap getFavicon() {
878 * Loads the tab if it's not loaded (e.g. because it was killed in background).
879 * @return true iff tab load was triggered
882 public boolean loadIfNeeded() {
887 * @return Whether or not the tab is in the closing process.
889 public boolean isClosing() {
894 * @param closing Whether or not the tab is in the closing process.
896 public void setClosing(boolean closing) {
897 mIsClosing = closing;
901 * @return The id of the tab that caused this tab to be opened.
903 public int getParentId() {
908 * @return Whether the tab should be grouped with its parent tab (true by default).
910 public boolean isGroupedWithParent() {
911 return mGroupedWithParent;
915 * Sets whether the tab should be grouped with its parent tab.
917 * @param groupedWithParent The new value.
918 * @see #isGroupedWithParent
920 public void setGroupedWithParent(boolean groupedWithParent) {
921 mGroupedWithParent = groupedWithParent;
924 private void destroyNativePageInternal(NativePage nativePage) {
925 if (nativePage == null) return;
926 assert nativePage != mNativePage : "Attempting to destroy active page.";
928 nativePage.destroy();
932 * Destroys the current {@link ContentView}.
933 * @param deleteNativeWebContents Whether or not to delete the native WebContents pointer.
935 protected final void destroyContentView(boolean deleteNativeWebContents) {
936 if (mContentView == null) return;
938 destroyContentViewInternal(mContentView);
940 if (mInfoBarContainer != null && mInfoBarContainer.getParent() != null) {
941 mInfoBarContainer.removeFromParentView();
943 if (mContentViewCore != null) mContentViewCore.destroy();
946 mContentViewCore = null;
947 mWebContentsDelegate = null;
948 mWebContentsObserver = null;
949 mVoiceSearchTabHelper = null;
951 assert mNativeTabAndroid != 0;
952 nativeDestroyWebContents(mNativeTabAndroid, deleteNativeWebContents);
956 * Gives subclasses the chance to clean up some state associated with this {@link ContentView}.
957 * This is because {@link #getContentView()} can return {@code null} if a {@link NativePage}
959 * @param contentView The {@link ContentView} that should have associated state cleaned up.
961 protected void destroyContentViewInternal(ContentView contentView) {
965 * A helper method to allow subclasses to build their own delegate.
966 * @return An instance of a {@link TabChromeWebContentsDelegateAndroid}.
968 protected TabChromeWebContentsDelegateAndroid createWebContentsDelegate() {
969 return new TabChromeWebContentsDelegateAndroid();
973 * A helper method to allow subclasses to build their own menu populator.
974 * @return An instance of a {@link ContextMenuPopulator}.
976 protected ContextMenuPopulator createContextMenuPopulator() {
977 return new ChromeContextMenuPopulator(new TabChromeContextMenuItemDelegate());
981 * @return The {@link WindowAndroid} associated with this {@link Tab}.
983 public WindowAndroid getWindowAndroid() {
984 return mWindowAndroid;
988 * @return The current {@link TabChromeWebContentsDelegateAndroid} instance.
990 protected TabChromeWebContentsDelegateAndroid getChromeWebContentsDelegateAndroid() {
991 return mWebContentsDelegate;
995 * Called when the favicon of the content this tab represents changes.
998 protected void onFaviconUpdated() {
999 for (TabObserver observer : mObservers) observer.onFaviconUpdated(this);
1003 * Called when the navigation entry containing the historyitem changed,
1004 * for example because of a scroll offset or form field change.
1007 protected void onNavEntryChanged() {
1011 * @return The native pointer representing the native side of this {@link Tab} object.
1014 protected long getNativePtr() {
1015 return mNativeTabAndroid;
1018 /** This is currently called when committing a pre-rendered page. */
1020 private void swapWebContents(
1021 final long newWebContents, boolean didStartLoad, boolean didFinishLoad) {
1022 swapContentView(ContentView.newInstance(mContext, newWebContents, getWindowAndroid()),
1023 false, didStartLoad, didFinishLoad);
1027 * Called to swap out the current view with the one passed in.
1028 * @param view The content view that should be swapped into the tab.
1029 * @param deleteOldNativeWebContents Whether to delete the native web contents of old view.
1030 * @param didStartLoad Whether WebContentsObserver::DidStartProvisionalLoadForFrame() has
1031 * already been called.
1032 * @param didFinishLoad Whether WebContentsObserver::DidFinishLoad() has already been called.
1034 protected void swapContentView(ContentView view, boolean deleteOldNativeWebContents,
1035 boolean didStartLoad, boolean didFinishLoad) {
1036 int originalWidth = 0;
1037 int originalHeight = 0;
1038 if (mContentViewCore != null) {
1039 originalWidth = mContentViewCore.getViewportWidthPix();
1040 originalHeight = mContentViewCore.getViewportHeightPix();
1041 mContentViewCore.onHide();
1043 destroyContentView(deleteOldNativeWebContents);
1044 NativePage previousNativePage = mNativePage;
1046 setContentView(view);
1047 // Size of the new ContentViewCore is zero at this point. If we don't call onSizeChanged(),
1048 // next onShow() call would send a resize message with the current ContentViewCore size
1049 // (zero) to the renderer process, although the new size will be set soon.
1050 // However, this size fluttering may confuse Blink and rendered result can be broken
1051 // (see http://crbug.com/340987).
1052 mContentViewCore.onSizeChanged(originalWidth, originalHeight, 0, 0);
1053 mContentViewCore.onShow();
1054 mContentViewCore.attachImeAdapter();
1055 for (TabObserver observer : mObservers) observer.onContentChanged(this);
1056 destroyNativePageInternal(previousNativePage);
1057 for (TabObserver observer : mObservers) {
1058 observer.onWebContentsSwapped(this, didStartLoad, didFinishLoad);
1063 private void clearNativePtr() {
1064 assert mNativeTabAndroid != 0;
1065 mNativeTabAndroid = 0;
1069 private void setNativePtr(long nativePtr) {
1070 assert mNativeTabAndroid == 0;
1071 mNativeTabAndroid = nativePtr;
1075 private long getNativeInfoBarContainer() {
1076 return getInfoBarContainer().getNative();
1080 * Validates {@code id} and increments the internal counter to make sure future ids don't
1082 * @param id The current id. Maybe {@link #INVALID_TAB_ID}.
1083 * @return A new id if {@code id} was {@link #INVALID_TAB_ID}, or {@code id}.
1085 private static int generateValidId(int id) {
1086 if (id == INVALID_TAB_ID) id = generateNextId();
1087 incrementIdCounterTo(id + 1);
1093 * @return An unused id.
1095 private static int generateNextId() {
1096 return sIdCounter.getAndIncrement();
1099 private void pushNativePageStateToNavigationEntry() {
1100 assert mNativeTabAndroid != 0 && getNativePage() != null;
1101 nativeSetActiveNavigationEntryTitleForUrl(mNativeTabAndroid, getNativePage().getUrl(),
1102 getNativePage().getTitle());
1106 * Ensures the counter is at least as high as the specified value. The counter should always
1107 * point to an unused ID (which will be handed out next time a request comes in). Exposed so
1108 * that anything externally loading tabs and ids can set enforce new tabs start at the correct
1110 * TODO(aurimas): Investigate reducing the visiblity of this method.
1111 * @param id The minimum id we should hand out to the next new tab.
1113 public static void incrementIdCounterTo(int id) {
1114 int diff = id - sIdCounter.get();
1115 if (diff <= 0) return;
1116 // It's possible idCounter has been incremented between the get above and the add below
1117 // but that's OK, because in the worst case we'll overly increment idCounter.
1118 sIdCounter.addAndGet(diff);
1121 private native void nativeInit();
1122 private native void nativeDestroy(long nativeTabAndroid);
1123 private native void nativeInitWebContents(long nativeTabAndroid, boolean incognito,
1124 ContentViewCore contentViewCore, ChromeWebContentsDelegateAndroid delegate,
1125 ContextMenuPopulator contextMenuPopulator);
1126 private native void nativeDestroyWebContents(long nativeTabAndroid, boolean deleteNative);
1127 private native WebContents nativeGetWebContents(long nativeTabAndroid);
1128 private native Profile nativeGetProfileAndroid(long nativeTabAndroid);
1129 private native int nativeLoadUrl(long nativeTabAndroid, String url, String extraHeaders,
1130 byte[] postData, int transition, String referrerUrl, int referrerPolicy);
1131 private native int nativeGetSecurityLevel(long nativeTabAndroid);
1132 private native void nativeSetActiveNavigationEntryTitleForUrl(long nativeTabAndroid, String url,
1134 private native boolean nativePrint(long nativeTabAndroid);