1 // Copyright 2012 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.chrome.browser.contextmenu.ChromeContextMenuItemDelegate;
17 import org.chromium.chrome.browser.contextmenu.ChromeContextMenuPopulator;
18 import org.chromium.chrome.browser.contextmenu.ContextMenuParams;
19 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulator;
20 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulatorWrapper;
21 import org.chromium.chrome.browser.contextmenu.EmptyChromeContextMenuItemDelegate;
22 import org.chromium.chrome.browser.infobar.AutoLoginProcessor;
23 import org.chromium.chrome.browser.infobar.InfoBarContainer;
24 import org.chromium.chrome.browser.profiles.Profile;
25 import org.chromium.chrome.browser.ui.toolbar.ToolbarModelSecurityLevel;
26 import org.chromium.content.browser.ContentView;
27 import org.chromium.content.browser.ContentViewClient;
28 import org.chromium.content.browser.ContentViewCore;
29 import org.chromium.content.browser.NavigationClient;
30 import org.chromium.content.browser.NavigationHistory;
31 import org.chromium.content.browser.PageInfo;
32 import org.chromium.content.browser.WebContentsObserverAndroid;
33 import org.chromium.content_public.browser.WebContents;
34 import org.chromium.ui.base.Clipboard;
35 import org.chromium.ui.base.WindowAndroid;
37 import java.util.concurrent.atomic.AtomicInteger;
40 * The basic Java representation of a tab. Contains and manages a {@link ContentView}.
42 * TabBase provides common functionality for ChromiumTestshell's Tab as well as Chrome on Android's
43 * tab. It is intended to be extended either on Java or both Java and C++, with ownership managed
46 * Extending just Java:
47 * - Just extend the class normally. Do not override initializeNative().
48 * Extending Java and C++:
49 * - Because of the inner-workings of JNI, the subclass is responsible for constructing the native
50 * subclass, which in turn constructs TabAndroid (the native counterpart to TabBase), which in
51 * turn sets the native pointer for TabBase. For destruction, subclasses in Java must clear
52 * their own native pointer reference, but TabBase#destroy() will handle deleting the native
55 public abstract class TabBase implements NavigationClient {
56 public static final int INVALID_TAB_ID = -1;
58 /** Used for automatically generating tab ids. */
59 private static final AtomicInteger sIdCounter = new AtomicInteger();
61 private long mNativeTabAndroid;
63 /** Unique id of this tab (within its container). */
64 private final int mId;
66 /** Whether or not this tab is an incognito tab. */
67 private final boolean mIncognito;
69 /** An Application {@link Context}. Unlike {@link #mContext}, this is the only one that is
70 * publicly exposed to help prevent leaking the {@link Activity}. */
71 private final Context mApplicationContext;
73 /** The {@link Context} used to create {@link View}s and other Android components. Unlike
74 * {@link #mApplicationContext}, this is not publicly exposed to help prevent leaking the
75 * {@link Activity}. */
76 private final Context mContext;
78 /** Gives {@link TabBase} a way to interact with the Android window. */
79 private final WindowAndroid mWindowAndroid;
81 /** The current native page (e.g. chrome-native://newtab), or {@code null} if there is none. */
82 private NativePage mNativePage;
84 /** The {@link ContentView} showing the current page or {@code null} if the tab is frozen. */
85 private ContentView mContentView;
87 /** InfoBar container to show InfoBars for this tab. */
88 private InfoBarContainer mInfoBarContainer;
90 /** The sync id of the TabBase if session sync is enabled. */
94 * The {@link ContentViewCore} for the current page, provided for convenience. This always
95 * equals {@link ContentView#getContentViewCore()}, or {@code null} if mContentView is
98 private ContentViewCore mContentViewCore;
101 * A list of TabBase observers. These are used to broadcast TabBase events to listeners.
103 private final ObserverList<TabObserver> mObservers = new ObserverList<TabObserver>();
105 // Content layer Observers and Delegates
106 private ContentViewClient mContentViewClient;
107 private WebContentsObserverAndroid mWebContentsObserver;
108 private VoiceSearchTabHelper mVoiceSearchTabHelper;
109 private TabBaseChromeWebContentsDelegateAndroid mWebContentsDelegate;
112 * A default {@link ChromeContextMenuItemDelegate} that supports some of the context menu
115 protected class TabBaseChromeContextMenuItemDelegate
116 extends EmptyChromeContextMenuItemDelegate {
117 private final Clipboard mClipboard;
120 * Builds a {@link TabBaseChromeContextMenuItemDelegate} instance.
122 public TabBaseChromeContextMenuItemDelegate() {
123 mClipboard = new Clipboard(getApplicationContext());
127 public boolean isIncognito() {
132 public void onSaveToClipboard(String text, boolean isUrl) {
133 mClipboard.setText(text, text);
137 public void onSaveImageToClipboard(String url) {
138 mClipboard.setHTMLText("<img src=\"" + url + "\">", url, url);
143 * A basic {@link ChromeWebContentsDelegateAndroid} that forwards some calls to the registered
144 * {@link TabObserver}s. Meant to be overridden by subclasses.
146 public class TabBaseChromeWebContentsDelegateAndroid
147 extends ChromeWebContentsDelegateAndroid {
149 public void onLoadProgressChanged(int progress) {
150 for (TabObserver observer : mObservers) {
151 observer.onLoadProgressChanged(TabBase.this, progress);
156 public void onUpdateUrl(String url) {
157 for (TabObserver observer : mObservers) observer.onUpdateUrl(TabBase.this, url);
161 public void showRepostFormWarningDialog(final ContentViewCore contentViewCore) {
162 RepostFormWarningDialog warningDialog = new RepostFormWarningDialog(
166 contentViewCore.cancelPendingReload();
171 contentViewCore.continuePendingReload();
174 Activity activity = (Activity) mContext;
175 warningDialog.show(activity.getFragmentManager(), null);
179 public void toggleFullscreenModeForTab(boolean enableFullscreen) {
180 for (TabObserver observer : mObservers) {
181 observer.onToggleFullscreenMode(TabBase.this, enableFullscreen);
186 public void navigationStateChanged(int flags) {
187 if ((flags & INVALIDATE_TYPE_TITLE) != 0) {
188 for (TabObserver observer : mObservers) observer.onTitleUpdated(TabBase.this);
190 if ((flags & INVALIDATE_TYPE_URL) != 0) {
191 for (TabObserver observer : mObservers) observer.onUrlUpdated(TabBase.this);
196 private class TabBaseContextMenuPopulator extends ContextMenuPopulatorWrapper {
197 public TabBaseContextMenuPopulator(ContextMenuPopulator populator) {
202 public void buildContextMenu(ContextMenu menu, Context context, ContextMenuParams params) {
203 super.buildContextMenu(menu, context, params);
204 for (TabObserver observer : mObservers) observer.onContextMenuShown(TabBase.this, menu);
208 private class TabBaseWebContentsObserverAndroid extends WebContentsObserverAndroid {
209 public TabBaseWebContentsObserverAndroid(ContentViewCore contentViewCore) {
210 super(contentViewCore);
214 public void navigationEntryCommitted() {
215 if (getNativePage() != null) {
216 pushNativePageStateToNavigationEntry();
221 public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode,
222 String description, String failingUrl) {
223 for (TabObserver observer : mObservers) {
224 observer.onDidFailLoad(TabBase.this, isProvisionalLoad, isMainFrame, errorCode,
225 description, failingUrl);
231 * Creates an instance of a {@link TabBase} with no id.
232 * @param incognito Whether or not this tab is incognito.
233 * @param context An instance of a {@link Context}.
234 * @param window An instance of a {@link WindowAndroid}.
236 public TabBase(boolean incognito, Context context, WindowAndroid window) {
237 this(INVALID_TAB_ID, incognito, context, window);
241 * Creates an instance of a {@link TabBase}.
242 * @param id The id this tab should be identified with.
243 * @param incognito Whether or not this tab is incognito.
244 * @param context An instance of a {@link Context}.
245 * @param window An instance of a {@link WindowAndroid}.
247 public TabBase(int id, boolean incognito, Context context, WindowAndroid window) {
248 // We need a valid Activity Context to build the ContentView with.
249 assert context == null || context instanceof Activity;
251 mId = generateValidId(id);
252 mIncognito = incognito;
253 // TODO(dtrainor): Only store application context here.
255 mApplicationContext = context != null ? context.getApplicationContext() : null;
256 mWindowAndroid = window;
260 * Adds a {@link TabObserver} to be notified on {@link TabBase} changes.
261 * @param observer The {@link TabObserver} to add.
263 public final void addObserver(TabObserver observer) {
264 mObservers.addObserver(observer);
268 * Removes a {@link TabObserver}.
269 * @param observer The {@link TabObserver} to remove.
271 public final void removeObserver(TabObserver observer) {
272 mObservers.removeObserver(observer);
276 * @return Whether or not this tab has a previous navigation entry.
278 public boolean canGoBack() {
279 return mContentViewCore != null && mContentViewCore.canGoBack();
283 * @return Whether or not this tab has a navigation entry after the current one.
285 public boolean canGoForward() {
286 return mContentViewCore != null && mContentViewCore.canGoForward();
290 * Goes to the navigation entry before the current one.
292 public void goBack() {
293 if (mContentViewCore != null) mContentViewCore.goBack();
297 * Goes to the navigation entry after the current one.
299 public void goForward() {
300 if (mContentViewCore != null) mContentViewCore.goForward();
304 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
305 if (mContentViewCore != null) {
306 return mContentViewCore.getDirectedNavigationHistory(isForward, itemLimit);
308 return new NavigationHistory();
313 public void goToNavigationIndex(int index) {
314 if (mContentViewCore != null) mContentViewCore.goToNavigationIndex(index);
318 * Loads the current navigation if there is a pending lazy load (after tab restore).
320 public void loadIfNecessary() {
321 if (mContentViewCore != null) mContentViewCore.loadIfNecessary();
325 * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
327 protected void requestRestoreLoad() {
328 if (mContentViewCore != null) mContentViewCore.requestRestoreLoad();
332 * @return Whether or not the {@link TabBase} is currently showing an interstitial page, such as
335 public boolean isShowingInterstitialPage() {
336 ContentViewCore contentViewCore = getContentViewCore();
337 return contentViewCore != null && contentViewCore.isShowingInterstitialPage();
341 * @return Whether or not the tab has something valid to render.
343 public boolean isReady() {
344 return mNativePage != null || (mContentViewCore != null && mContentViewCore.isReady());
348 * @return The {@link View} displaying the current page in the tab. This might be a
349 * {@link ContentView} but could potentially be any instance of {@link View}. This can
350 * be {@code null}, if the tab is frozen or being initialized or destroyed.
352 public View getView() {
353 PageInfo pageInfo = getPageInfo();
354 return pageInfo != null ? pageInfo.getView() : null;
358 * @return The width of the content of this tab. Can be 0 if there is no content.
360 public int getWidth() {
361 View view = getView();
362 return view != null ? view.getWidth() : 0;
366 * @return The height of the content of this tab. Can be 0 if there is no content.
368 public int getHeight() {
369 View view = getView();
370 return view != null ? view.getHeight() : 0;
374 * @return The application {@link Context} associated with this tab.
376 protected Context getApplicationContext() {
377 return mApplicationContext;
381 * @return The infobar container.
383 public final InfoBarContainer getInfoBarContainer() {
384 return mInfoBarContainer;
388 * Create an {@code AutoLoginProcessor} to decide how to handle login
391 protected abstract AutoLoginProcessor createAutoLoginProcessor();
394 * Prints the current page.
396 * @return Whether the printing process is started successfully.
398 public boolean print() {
399 assert mNativeTabAndroid != 0;
400 return nativePrint(mNativeTabAndroid);
404 * Reloads the current page content if it is a {@link ContentView}.
406 public void reload() {
407 // TODO(dtrainor): Should we try to rebuild the ContentView if it's frozen?
408 if (mContentViewCore != null) mContentViewCore.reload(true);
412 * Reloads the current page content if it is a {@link ContentView}.
413 * This version ignores the cache and reloads from the network.
415 public void reloadIgnoringCache() {
416 if (mContentViewCore != null) mContentViewCore.reloadIgnoringCache(true);
419 /** Stop the current navigation. */
420 public void stopLoading() {
421 if (mContentViewCore != null) mContentViewCore.stopLoading();
425 * @return The background color of the tab.
427 public int getBackgroundColor() {
428 return getPageInfo() != null ? getPageInfo().getBackgroundColor() : Color.WHITE;
432 * @return The web contents associated with this tab.
434 public WebContents getWebContents() {
435 if (mNativeTabAndroid == 0) return null;
436 return nativeGetWebContents(mNativeTabAndroid);
440 * @return The profile associated with this tab.
442 public Profile getProfile() {
443 if (mNativeTabAndroid == 0) return null;
444 return nativeGetProfileAndroid(mNativeTabAndroid);
448 * @return The id representing this tab.
456 * @return Whether or not this tab is incognito.
458 public boolean isIncognito() {
463 * @return The {@link ContentView} associated with the current page, or {@code null} if
464 * there is no current page or the current page is displayed using something besides a
465 * {@link ContentView}.
467 public ContentView getContentView() {
468 return mNativePage == null ? mContentView : null;
472 * @return The {@link ContentViewCore} associated with the current page, or {@code null} if
473 * there is no current page or the current page is displayed using something besides a
474 * {@link ContentView}.
476 public ContentViewCore getContentViewCore() {
477 return mNativePage == null ? mContentViewCore : null;
481 * @return A {@link PageInfo} describing the current page. This is always not {@code null}
482 * except during initialization, destruction, and when the tab is frozen.
484 public PageInfo getPageInfo() {
485 return mNativePage != null ? mNativePage : mContentView;
489 * @return The {@link NativePage} associated with the current page, or {@code null} if there is
490 * no current page or the current page is displayed using something besides
491 * {@link NativePage}.
493 public NativePage getNativePage() {
498 * @return Whether or not the {@link TabBase} represents a {@link NativePage}.
500 public boolean isNativePage() {
501 return mNativePage != null;
505 * Set whether or not the {@link ContentViewCore} should be using a desktop user agent for the
506 * currently loaded page.
507 * @param useDesktop If {@code true}, use a desktop user agent. Otherwise use a mobile one.
508 * @param reloadOnChange Reload the page if the user agent has changed.
510 public void setUseDesktopUserAgent(boolean useDesktop, boolean reloadOnChange) {
511 if (mContentViewCore != null) {
512 mContentViewCore.setUseDesktopUserAgent(useDesktop, reloadOnChange);
517 * @return Whether or not the {@link ContentViewCore} is using a desktop user agent.
519 public boolean getUseDesktopUserAgent() {
520 return mContentViewCore != null && mContentViewCore.getUseDesktopUserAgent();
524 * @return The current {ToolbarModelSecurityLevel} for the tab.
526 public int getSecurityLevel() {
527 if (mNativeTabAndroid == 0) return ToolbarModelSecurityLevel.NONE;
528 return nativeGetSecurityLevel(mNativeTabAndroid);
532 * @return The sync id of the tab if session sync is enabled, {@code 0} otherwise.
535 protected int getSyncId() {
540 * @param syncId The sync id of the tab if session sync is enabled.
543 protected void setSyncId(int syncId) {
548 * @return An {@link ObserverList.RewindableIterator} instance that points to all of
549 * the current {@link TabObserver}s on this class. Note that calling
550 * {@link java.util.Iterator#remove()} will throw an
551 * {@link UnsupportedOperationException}.
553 protected ObserverList.RewindableIterator<TabObserver> getTabObservers() {
554 return mObservers.rewindableIterator();
558 * @return The {@link ContentViewClient} currently bound to any {@link ContentViewCore}
559 * associated with the current page. There can still be a {@link ContentViewClient}
560 * even when there is no {@link ContentViewCore}.
562 protected ContentViewClient getContentViewClient() {
563 return mContentViewClient;
567 * @param client The {@link ContentViewClient} to be bound to any current or new
568 * {@link ContentViewCore}s associated with this {@link TabBase}.
570 protected void setContentViewClient(ContentViewClient client) {
571 if (mContentViewClient == client) return;
573 ContentViewClient oldClient = mContentViewClient;
574 mContentViewClient = client;
576 if (mContentViewCore == null) return;
578 if (mContentViewClient != null) {
579 mContentViewCore.setContentViewClient(mContentViewClient);
580 } else if (oldClient != null) {
581 // We can't set a null client, but we should clear references to the last one.
582 mContentViewCore.setContentViewClient(new ContentViewClient());
587 * Shows the given {@code nativePage} if it's not already showing.
588 * @param nativePage The {@link NativePage} to show.
590 protected void showNativePage(NativePage nativePage) {
591 if (mNativePage == nativePage) return;
592 NativePage previousNativePage = mNativePage;
593 mNativePage = nativePage;
594 pushNativePageStateToNavigationEntry();
595 for (TabObserver observer : mObservers) observer.onContentChanged(this);
596 destroyNativePageInternal(previousNativePage);
600 * Hides the current {@link NativePage}, if any, and shows the {@link ContentView}.
602 protected void showRenderedPage() {
603 if (mNativePage == null) return;
604 NativePage previousNativePage = mNativePage;
606 for (TabObserver observer : mObservers) observer.onContentChanged(this);
607 destroyNativePageInternal(previousNativePage);
611 * Initializes this {@link TabBase}.
613 public void initialize() {
618 * Builds the native counterpart to this class. Meant to be overridden by subclasses to build
619 * subclass native counterparts instead. Subclasses should not call this via super and instead
620 * rely on the native class to create the JNI association.
622 protected void initializeNative() {
623 if (mNativeTabAndroid == 0) nativeInit();
624 assert mNativeTabAndroid != 0;
628 * A helper method to initialize a {@link ContentView} without any native WebContents pointer.
630 protected final void initContentView() {
631 initContentView(ContentViewUtil.createNativeWebContents(mIncognito));
635 * Completes the {@link ContentView} specific initialization around a native WebContents
636 * pointer. {@link #getPageInfo()} will still return the {@link NativePage} if there is one.
637 * All initialization that needs to reoccur after a web contents swap should be added here.
639 * NOTE: If you attempt to pass a native WebContents that does not have the same incognito
640 * state as this tab this call will fail.
642 * @param nativeWebContents The native web contents pointer.
644 protected void initContentView(long nativeWebContents) {
645 NativePage previousNativePage = mNativePage;
647 destroyNativePageInternal(previousNativePage);
649 mContentView = ContentView.newInstance(mContext, nativeWebContents, getWindowAndroid());
651 mContentViewCore = mContentView.getContentViewCore();
652 mWebContentsDelegate = createWebContentsDelegate();
653 mWebContentsObserver = new TabBaseWebContentsObserverAndroid(mContentViewCore);
654 mVoiceSearchTabHelper = new VoiceSearchTabHelper(mContentViewCore);
656 if (mContentViewClient != null) mContentViewCore.setContentViewClient(mContentViewClient);
658 assert mNativeTabAndroid != 0;
659 nativeInitWebContents(
660 mNativeTabAndroid, mIncognito, mContentViewCore, mWebContentsDelegate,
661 new TabBaseContextMenuPopulator(createContextMenuPopulator()));
663 // In the case where restoring a Tab or showing a prerendered one we already have a
664 // valid infobar container, no need to recreate one.
665 if (mInfoBarContainer == null) {
666 // The InfoBarContainer needs to be created after the ContentView has been natively
668 mInfoBarContainer = new InfoBarContainer(
669 (Activity) mContext, createAutoLoginProcessor(), getId(), getContentView(),
672 mInfoBarContainer.onParentViewChanged(getId(), getContentView());
677 * Cleans up all internal state, destroying any {@link NativePage} or {@link ContentView}
678 * currently associated with this {@link TabBase}. This also destroys the native counterpart
679 * to this class, which means that all subclasses should erase their native pointers after
680 * this method is called. Once this call is made this {@link TabBase} should no longer be used.
682 public void destroy() {
683 for (TabObserver observer : mObservers) observer.onDestroyed(this);
685 NativePage currentNativePage = mNativePage;
687 destroyNativePageInternal(currentNativePage);
688 destroyContentView(true);
690 // Destroys the native tab after destroying the ContentView but before destroying the
691 // InfoBarContainer. The native tab should be destroyed before the infobar container as
692 // destroying the native tab cleanups up any remaining infobars. The infobar container
693 // expects all infobars to be cleaned up before its own destruction.
694 assert mNativeTabAndroid != 0;
695 nativeDestroy(mNativeTabAndroid);
696 assert mNativeTabAndroid == 0;
698 if (mInfoBarContainer != null) {
699 mInfoBarContainer.destroy();
700 mInfoBarContainer = null;
705 * @return Whether or not this Tab has a live native component.
707 public boolean isInitialized() {
708 return mNativeTabAndroid != 0;
712 * @return The url associated with the tab.
715 public String getUrl() {
716 return mContentView != null ? mContentView.getUrl() : "";
720 * @return The tab title.
723 public String getTitle() {
724 return getPageInfo() != null ? getPageInfo().getTitle() : "";
728 * @return The bitmap of the favicon scaled to 16x16dp. null if no favicon
729 * is specified or it requires the default favicon.
730 * TODO(bauerb): Upstream implementation.
732 public Bitmap getFavicon() {
737 * Restores the tab if it is frozen or crashed.
738 * @return true iff tab restore was triggered.
741 public boolean restoreIfNeeded() {
745 private void destroyNativePageInternal(NativePage nativePage) {
746 if (nativePage == null) return;
747 assert getPageInfo() != nativePage : "Attempting to destroy active page.";
749 nativePage.destroy();
753 * Destroys the current {@link ContentView}.
754 * @param deleteNativeWebContents Whether or not to delete the native WebContents pointer.
756 protected final void destroyContentView(boolean deleteNativeWebContents) {
757 if (mContentView == null) return;
759 destroyContentViewInternal(mContentView);
761 if (mInfoBarContainer != null && mInfoBarContainer.getParent() != null) {
762 mInfoBarContainer.removeFromParentView();
764 if (mContentViewCore != null) mContentViewCore.destroy();
767 mContentViewCore = null;
768 mWebContentsDelegate = null;
769 mWebContentsObserver = null;
770 mVoiceSearchTabHelper = null;
772 assert mNativeTabAndroid != 0;
773 nativeDestroyWebContents(mNativeTabAndroid, deleteNativeWebContents);
777 * Gives subclasses the chance to clean up some state associated with this {@link ContentView}.
778 * This is because {@link #getContentView()} can return {@code null} if a {@link NativePage}
780 * @param contentView The {@link ContentView} that should have associated state cleaned up.
782 protected void destroyContentViewInternal(ContentView contentView) {
786 * A helper method to allow subclasses to build their own delegate.
787 * @return An instance of a {@link TabBaseChromeWebContentsDelegateAndroid}.
789 protected TabBaseChromeWebContentsDelegateAndroid createWebContentsDelegate() {
790 return new TabBaseChromeWebContentsDelegateAndroid();
794 * A helper method to allow subclasses to build their own menu populator.
795 * @return An instance of a {@link ContextMenuPopulator}.
797 protected ContextMenuPopulator createContextMenuPopulator() {
798 return new ChromeContextMenuPopulator(new TabBaseChromeContextMenuItemDelegate());
802 * @return The {@link WindowAndroid} associated with this {@link TabBase}.
804 protected WindowAndroid getWindowAndroid() {
805 return mWindowAndroid;
809 * @return The current {@link TabBaseChromeWebContentsDelegateAndroid} instance.
811 protected TabBaseChromeWebContentsDelegateAndroid getChromeWebContentsDelegateAndroid() {
812 return mWebContentsDelegate;
816 * Called when the favicon of the content this tab represents changes.
819 protected void onFaviconUpdated() {
820 for (TabObserver observer : mObservers) observer.onFaviconUpdated(this);
824 * @return The native pointer representing the native side of this {@link TabBase} object.
827 protected long getNativePtr() {
828 return mNativeTabAndroid;
831 /** This is currently called when committing a pre-rendered page. */
833 private void swapWebContents(final long newWebContents) {
834 if (mContentViewCore != null) mContentViewCore.onHide();
835 destroyContentView(false);
836 NativePage previousNativePage = mNativePage;
838 initContentView(newWebContents);
839 mContentViewCore.onShow();
840 mContentViewCore.attachImeAdapter();
841 for (TabObserver observer : mObservers) observer.onContentChanged(this);
842 destroyNativePageInternal(previousNativePage);
843 for (TabObserver observer : mObservers) observer.onWebContentsSwapped(this);
847 private void clearNativePtr() {
848 assert mNativeTabAndroid != 0;
849 mNativeTabAndroid = 0;
853 private void setNativePtr(long nativePtr) {
854 assert mNativeTabAndroid == 0;
855 mNativeTabAndroid = nativePtr;
859 private long getNativeInfoBarContainer() {
860 return getInfoBarContainer().getNative();
864 * Validates {@code id} and increments the internal counter to make sure future ids don't
866 * @param id The current id. Maybe {@link #INVALID_TAB_ID}.
867 * @return A new id if {@code id} was {@link #INVALID_TAB_ID}, or {@code id}.
869 private static int generateValidId(int id) {
870 if (id == INVALID_TAB_ID) id = generateNextId();
871 incrementIdCounterTo(id + 1);
877 * @return An unused id.
879 private static int generateNextId() {
880 return sIdCounter.getAndIncrement();
883 private void pushNativePageStateToNavigationEntry() {
884 assert mNativeTabAndroid != 0 && getNativePage() != null;
885 nativeSetActiveNavigationEntryTitleForUrl(mNativeTabAndroid, getNativePage().getUrl(),
886 getNativePage().getTitle());
890 * Ensures the counter is at least as high as the specified value. The counter should always
891 * point to an unused ID (which will be handed out next time a request comes in). Exposed so
892 * that anything externally loading tabs and ids can set enforce new tabs start at the correct
894 * TODO(aurimas): Investigate reducing the visiblity of this method.
895 * @param id The minimum id we should hand out to the next new tab.
897 public static void incrementIdCounterTo(int id) {
898 int diff = id - sIdCounter.get();
899 if (diff <= 0) return;
900 // It's possible idCounter has been incremented between the get above and the add below
901 // but that's OK, because in the worst case we'll overly increment idCounter.
902 sIdCounter.addAndGet(diff);
905 private native void nativeInit();
906 private native void nativeDestroy(long nativeTabAndroid);
907 private native void nativeInitWebContents(long nativeTabAndroid, boolean incognito,
908 ContentViewCore contentViewCore, ChromeWebContentsDelegateAndroid delegate,
909 ContextMenuPopulator contextMenuPopulator);
910 private native void nativeDestroyWebContents(long nativeTabAndroid, boolean deleteNative);
911 private native WebContents nativeGetWebContents(long nativeTabAndroid);
912 private native Profile nativeGetProfileAndroid(long nativeTabAndroid);
913 private native int nativeGetSecurityLevel(long nativeTabAndroid);
914 private native void nativeSetActiveNavigationEntryTitleForUrl(long nativeTabAndroid, String url,
916 private native boolean nativePrint(long nativeTabAndroid);