Index: chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelBase.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelBase.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0e2b0a263e87921c077a72207b8c656c6d1035c3 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelBase.java |
@@ -0,0 +1,638 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.tabmodel; |
+ |
+import org.chromium.base.CalledByNative; |
+import org.chromium.base.ObserverList; |
+import org.chromium.base.TraceEvent; |
+import org.chromium.chrome.browser.Tab; |
+import org.chromium.chrome.browser.profiles.Profile; |
+import org.chromium.chrome.browser.util.MathUtils; |
+ |
+import java.util.ArrayList; |
+import java.util.List; |
+ |
+/** |
+ * This is the default implementation of the {@link TabModel} interface. |
+ */ |
+public abstract class TabModelBase implements TabModel { |
+ private static final String TAG = "TabModelBase"; |
+ |
+ /** |
+ * The main list of tabs. Note that when this changes, all pending closures must be committed |
+ * via {@link #commitAllTabClosures()} as the indices are no longer valid. Also |
+ * {@link RewoundList#resetRewoundState()} must be called so that the full model will be up to |
+ * date. |
+ */ |
+ private final List<Tab> mTabs = new ArrayList<Tab>(); |
+ |
+ private final boolean mIsIncognito; |
+ |
+ private final TabModelOrderController mOrderController; |
+ |
+ protected final TabModelDelegate mModelDelegate; |
+ |
+ private final ObserverList<TabModelObserver> mObservers; |
+ |
+ // Undo State Tracking ------------------------------------------------------------------------- |
+ |
+ /** |
+ * A {@link TabList} that represents the complete list of {@link Tab}s. This is so that |
+ * certain UI elements can call {@link TabModel#getComprehensiveModel()} to get a full list of |
+ * {@link Tab}s that includes rewindable entries, as the typical {@link TabModel} does not |
+ * return rewindable entries. |
+ */ |
+ private final RewoundList mRewoundList = new RewoundList(); |
+ |
+ /** |
+ * This specifies the current {@link Tab} in {@link #mTabs}. |
+ */ |
+ private int mIndex = INVALID_TAB_INDEX; |
+ |
+ /** Native Tab pointer which will be set by nativeInit(). */ |
+ private long mNativeTabModelImpl = 0; |
+ |
+ public TabModelBase(boolean incognito, TabModelOrderController orderController, |
+ TabModelDelegate modelDelegate) { |
+ mIsIncognito = incognito; |
+ mNativeTabModelImpl = nativeInit(incognito); |
+ mOrderController = orderController; |
+ mModelDelegate = modelDelegate; |
+ mObservers = new ObserverList<TabModelObserver>(); |
+ } |
+ |
+ @Override |
+ public Profile getProfile() { |
+ return nativeGetProfileAndroid(mNativeTabModelImpl); |
+ } |
+ |
+ @Override |
+ public boolean isIncognito() { |
+ return mIsIncognito; |
+ } |
+ |
+ @Override |
+ public void destroy() { |
+ for (Tab tab : mTabs) { |
+ if (tab.isInitialized()) tab.destroy(); |
+ } |
+ |
+ mRewoundList.destroy(); |
+ |
+ if (mNativeTabModelImpl != 0) { |
+ nativeDestroy(mNativeTabModelImpl); |
+ mNativeTabModelImpl = 0; |
+ } |
+ } |
+ |
+ @Override |
+ public void addObserver(TabModelObserver observer) { |
+ mObservers.addObserver(observer); |
+ } |
+ |
+ @Override |
+ public void removeObserver(TabModelObserver observer) { |
+ mObservers.removeObserver(observer); |
+ } |
+ |
+ /** |
+ * Initializes the newly created tab, adds it to controller, and dispatches creation |
+ * step notifications. |
+ */ |
+ @Override |
+ public void addTab(Tab tab, int index, TabLaunchType type) { |
+ TraceEvent.begin(); |
+ |
+ for (TabModelObserver obs : mObservers) obs.willAddTab(tab, type); |
+ |
+ boolean selectTab = mOrderController.willOpenInForeground(type, mIsIncognito); |
+ |
+ index = mOrderController.determineInsertionIndex(type, index, tab); |
+ assert index <= mTabs.size(); |
+ |
+ assert tab.isIncognito() == mIsIncognito; |
+ |
+ // TODO(dtrainor): Update the list of undoable tabs instead of committing it. |
+ commitAllTabClosures(); |
+ |
+ if (index < 0 || index > mTabs.size()) { |
+ mTabs.add(tab); |
+ } else { |
+ mTabs.add(index, tab); |
+ if (index <= mIndex) { |
+ mIndex++; |
+ } |
+ } |
+ |
+ if (!isCurrentModel()) { |
+ // When adding new tabs in the background, make sure we set a valid index when the |
+ // first one is added. When in the foreground, calls to setIndex will take care of |
+ // this. |
+ mIndex = Math.max(mIndex, 0); |
+ } |
+ |
+ mRewoundList.resetRewoundState(); |
+ |
+ int newIndex = indexOf(tab); |
+ mModelDelegate.didChange(); |
+ mModelDelegate.didCreateNewTab(tab); |
+ |
+ if (mNativeTabModelImpl != 0) nativeTabAddedToModel(mNativeTabModelImpl, tab); |
+ |
+ for (TabModelObserver obs : mObservers) obs.didAddTab(tab, type); |
+ |
+ if (selectTab) { |
+ mModelDelegate.selectModel(mIsIncognito); |
+ setIndex(newIndex, TabModel.TabSelectionType.FROM_NEW); |
+ } |
+ |
+ TraceEvent.end(); |
+ } |
+ |
+ @Override |
+ public void moveTab(int id, int newIndex) { |
+ newIndex = MathUtils.clamp(newIndex, 0, mTabs.size()); |
+ |
+ int curIndex = TabModelUtils.getTabIndexById(this, id); |
+ |
+ if (curIndex == INVALID_TAB_INDEX || curIndex == newIndex || curIndex + 1 == newIndex) { |
+ return; |
+ } |
+ |
+ // TODO(dtrainor): Update the list of undoable tabs instead of committing it. |
+ commitAllTabClosures(); |
+ |
+ Tab tab = mTabs.remove(curIndex); |
+ if (curIndex < newIndex) --newIndex; |
+ |
+ mTabs.add(newIndex, tab); |
+ |
+ if (curIndex == mIndex) { |
+ mIndex = newIndex; |
+ } else if (curIndex < mIndex && newIndex >= mIndex) { |
+ --mIndex; |
+ } else if (curIndex > mIndex && newIndex <= mIndex) { |
+ ++mIndex; |
+ } |
+ |
+ mRewoundList.resetRewoundState(); |
+ |
+ mModelDelegate.didChange(); |
+ for (TabModelObserver obs : mObservers) obs.didMoveTab(tab, newIndex, curIndex); |
+ } |
+ |
+ @Override |
+ @CalledByNative |
+ public boolean closeTab(Tab tab) { |
+ return closeTab(tab, true, false, false); |
+ } |
+ |
+ private Tab findTabInAllTabModels(int tabId) { |
+ Tab tab = TabModelUtils.getTabById(mModelDelegate.getModel(mIsIncognito), tabId); |
+ if (tab != null) return tab; |
+ return TabModelUtils.getTabById(mModelDelegate.getModel(!mIsIncognito), tabId); |
+ } |
+ |
+ @Override |
+ public Tab getNextTabIfClosed(int id) { |
+ Tab tabToClose = TabModelUtils.getTabById(this, id); |
+ Tab currentTab = TabModelUtils.getCurrentTab(this); |
+ if (tabToClose == null) return currentTab; |
+ |
+ int closingTabIndex = indexOf(tabToClose); |
+ Tab adjacentTab = getTabAt((closingTabIndex == 0) ? 1 : closingTabIndex - 1); |
+ Tab parentTab = findTabInAllTabModels(tabToClose.getParentId()); |
+ |
+ // Determine which tab to select next according to these rules: |
+ // * If closing a background tab, keep the current tab selected. |
+ // * Otherwise, if not in overview mode, select the parent tab if it exists. |
+ // * Otherwise, select an adjacent tab if one exists. |
+ // * Otherwise, if closing the last incognito tab, select the current normal tab. |
+ // * Otherwise, select nothing. |
+ Tab nextTab = null; |
+ if (tabToClose != currentTab && currentTab != null) { |
+ nextTab = currentTab; |
+ } else if (parentTab != null && !mModelDelegate.isInOverviewMode()) { |
+ nextTab = parentTab; |
+ } else if (adjacentTab != null) { |
+ nextTab = adjacentTab; |
+ } else if (mIsIncognito) { |
+ nextTab = TabModelUtils.getCurrentTab(mModelDelegate.getModel(false)); |
+ } |
+ |
+ return nextTab; |
+ } |
+ |
+ @Override |
+ public boolean isClosurePending(int tabId) { |
+ return mRewoundList.getPendingRewindTab(tabId) != null; |
+ } |
+ |
+ @Override |
+ public boolean supportsPendingClosures() { |
+ return !mIsIncognito; |
+ } |
+ |
+ @Override |
+ public TabList getComprehensiveModel() { |
+ if (!supportsPendingClosures()) return this; |
+ return mRewoundList; |
+ } |
+ |
+ @Override |
+ public void cancelTabClosure(int tabId) { |
+ Tab tab = mRewoundList.getPendingRewindTab(tabId); |
+ if (tab == null) return; |
+ |
+ tab.setClosing(false); |
+ |
+ // Find a valid previous tab entry so we know what tab to insert after. With the following |
+ // example, calling cancelTabClosure(4) would need to know to insert after 2. So we have to |
+ // track across mRewoundTabs and mTabs and see what the last valid mTabs entry was (2) when |
+ // we hit the 4 in the rewound list. An insertIndex of -1 represents the beginning of the |
+ // list, as this is the index of tab to insert after. |
+ // mTabs: 0 2 5 |
+ // mRewoundTabs 0 1 2 3 4 5 |
+ int prevIndex = -1; |
+ final int stopIndex = mRewoundList.indexOf(tab); |
+ for (int rewoundIndex = 0; rewoundIndex < stopIndex; rewoundIndex++) { |
+ Tab rewoundTab = mRewoundList.getTabAt(rewoundIndex); |
+ if (prevIndex == mTabs.size() - 1) break; |
+ if (rewoundTab == mTabs.get(prevIndex + 1)) prevIndex++; |
+ } |
+ |
+ // Figure out where to insert the tab. Just add one to prevIndex, as -1 represents the |
+ // beginning of the list, so we'll insert at 0. |
+ int insertIndex = prevIndex + 1; |
+ if (mIndex >= insertIndex) mIndex++; |
+ mTabs.add(insertIndex, tab); |
+ |
+ boolean activeModel = mModelDelegate.getCurrentModel() == this; |
+ |
+ // If we're the active model call setIndex to actually select this tab, otherwise just set |
+ // mIndex but don't kick off everything that happens when calling setIndex(). |
+ if (activeModel) { |
+ setIndex(insertIndex); |
+ } else { |
+ mIndex = insertIndex; |
+ } |
+ |
+ for (TabModelObserver obs : mObservers) obs.tabClosureUndone(tab); |
+ } |
+ |
+ @Override |
+ public void commitTabClosure(int tabId) { |
+ Tab tab = mRewoundList.getPendingRewindTab(tabId); |
+ if (tab == null) return; |
+ |
+ // We're committing the close, actually remove it from the lists and finalize the closing |
+ // operation. |
+ mRewoundList.removeTab(tab); |
+ finalizeTabClosure(tab); |
+ for (TabModelObserver obs : mObservers) obs.tabClosureCommitted(tab); |
+ } |
+ |
+ @Override |
+ public void commitAllTabClosures() { |
+ while (mRewoundList.getCount() > mTabs.size()) { |
+ commitTabClosure(mRewoundList.getNextRewindableTab().getId()); |
+ } |
+ |
+ assert !mRewoundList.hasPendingClosures(); |
+ } |
+ |
+ @Override |
+ public boolean closeTab(Tab tabToClose, boolean animate, boolean uponExit, boolean canUndo) { |
+ if (tabToClose == null) { |
+ assert false : "Tab is null!"; |
+ return false; |
+ } |
+ |
+ if (!mTabs.contains(tabToClose)) { |
+ assert false : "Tried to close a tab from another model!"; |
+ return false; |
+ } |
+ |
+ canUndo &= supportsPendingClosures(); |
+ |
+ if (canUndo) { |
+ for (TabModelObserver obs : mObservers) obs.tabPendingClosure(tabToClose); |
+ } |
+ startTabClosure(tabToClose, animate, uponExit, canUndo); |
+ if (!canUndo) finalizeTabClosure(tabToClose); |
+ |
+ return true; |
+ } |
+ |
+ @Override |
+ public void closeAllTabs() { |
+ commitAllTabClosures(); |
+ |
+ while (getCount() > 0) { |
+ TabModelUtils.closeTabByIndex(this, 0); |
+ } |
+ } |
+ |
+ @Override |
+ @CalledByNative |
+ public Tab getTabAt(int index) { |
+ // This will catch INVALID_TAB_INDEX and return null |
+ if (index < 0 || index >= mTabs.size()) return null; |
+ return mTabs.get(index); |
+ } |
+ |
+ // Index of the given tab in the order of the tab stack. |
+ @Override |
+ public int indexOf(Tab tab) { |
+ return mTabs.indexOf(tab); |
+ } |
+ |
+ /** |
+ * @return true if this is the current model according to the model selector |
+ */ |
+ private boolean isCurrentModel() { |
+ return mModelDelegate.getCurrentModel() == this; |
+ } |
+ |
+ // TODO(aurimas): Move this method to TabModelSelector when notifications move there. |
+ private int getLastId(TabSelectionType type) { |
+ if (type == TabSelectionType.FROM_CLOSE) return Tab.INVALID_TAB_ID; |
+ |
+ // Get the current tab in the current tab model. |
+ Tab currentTab = TabModelUtils.getCurrentTab(mModelDelegate.getCurrentModel()); |
+ return currentTab != null ? currentTab.getId() : Tab.INVALID_TAB_ID; |
+ } |
+ |
+ // This function is complex and its behavior depends on persisted state, including mIndex. |
+ @Override |
+ public void setIndex(int i, final TabSelectionType type) { |
+ TraceEvent.begin(); |
+ int lastId = getLastId(type); |
+ |
+ if (!isCurrentModel()) { |
+ mModelDelegate.selectModel(isIncognito()); |
+ } |
+ |
+ if (mTabs.size() <= 0) { |
+ mIndex = INVALID_TAB_INDEX; |
+ } else { |
+ mIndex = MathUtils.clamp(i, 0, mTabs.size() - 1); |
+ } |
+ |
+ Tab tab = TabModelUtils.getCurrentTab(this); |
+ |
+ mModelDelegate.requestToShowTab(tab, type); |
+ |
+ if (tab != null) { |
+ for (TabModelObserver obs : mObservers) obs.didSelectTab(tab, type, lastId); |
+ } |
+ |
+ // notifyDataSetChanged() can call into |
+ // ChromeViewHolderTablet.handleTabChangeExternal(), which will eventually move the |
+ // ContentView onto the current view hierarchy (with addView()). |
+ mModelDelegate.didChange(); |
+ TraceEvent.end(); |
+ } |
+ |
+ /** |
+ * @param incognito |
+ * @param nativeWebContents |
+ * @param parentId |
+ * @return |
+ */ |
+ @CalledByNative |
+ protected abstract Tab createTabWithNativeContents(boolean incognito, long nativeWebContents, |
+ int parentId); |
+ |
+ /** |
+ * Performs the necessary actions to remove this {@link Tab} from this {@link TabModel}. |
+ * This does not actually destroy the {@link Tab} (see |
+ * {@link #finalizeTabClosure(Tab)}. |
+ * |
+ * @param tab The {@link Tab} to remove from this {@link TabModel}. |
+ * @param animate Whether or not to animate the closing. |
+ * @param uponExit Whether or not this is closing while the Activity is exiting. |
+ * @param canUndo Whether or not this operation can be undone. Note that if this is {@code true} |
+ * and {@link #supportsPendingClosures()} is {@code true}, |
+ * {@link #commitTabClosure(int)} or {@link #commitAllTabClosures()} needs to be |
+ * called to actually delete and clean up {@code tab}. |
+ */ |
+ private void startTabClosure(Tab tab, boolean animate, boolean uponExit, boolean canUndo) { |
+ final int closingTabId = tab.getId(); |
+ final int closingTabIndex = indexOf(tab); |
+ |
+ tab.setClosing(true); |
+ |
+ for (TabModelObserver obs : mObservers) obs.willCloseTab(tab, animate); |
+ |
+ Tab currentTab = TabModelUtils.getCurrentTab(this); |
+ Tab adjacentTab = getTabAt(closingTabIndex == 0 ? 1 : closingTabIndex - 1); |
+ Tab nextTab = getNextTabIfClosed(closingTabId); |
+ |
+ // TODO(dtrainor): Update the list of undoable tabs instead of committing it. |
+ if (!canUndo) commitAllTabClosures(); |
+ mTabs.remove(tab); |
+ |
+ boolean nextIsIncognito = nextTab == null ? false : nextTab.isIncognito(); |
+ int nextTabId = nextTab == null ? Tab.INVALID_TAB_ID : nextTab.getId(); |
+ int nextTabIndex = nextTab == null ? INVALID_TAB_INDEX : TabModelUtils.getTabIndexById( |
+ mModelDelegate.getModel(nextIsIncognito), nextTabId); |
+ |
+ if (nextTab != currentTab) { |
+ if (nextIsIncognito != isIncognito()) mIndex = indexOf(adjacentTab); |
+ |
+ TabModel nextModel = mModelDelegate.getModel(nextIsIncognito); |
+ nextModel.setIndex(nextTabIndex, |
+ uponExit ? TabSelectionType.FROM_EXIT : TabSelectionType.FROM_CLOSE); |
+ } else { |
+ mIndex = nextTabIndex; |
+ } |
+ |
+ if (!canUndo) mRewoundList.resetRewoundState(); |
+ } |
+ |
+ /** |
+ * Actually closes and cleans up {@code tab}. |
+ * @param tab The {@link Tab} to close. |
+ */ |
+ private void finalizeTabClosure(Tab tab) { |
+ for (TabModelObserver obs : mObservers) obs.didCloseTab(tab); |
+ tab.destroy(); |
+ } |
+ |
+ private class RewoundList implements TabList { |
+ /** |
+ * A list of {@link Tab}s that represents the completely rewound list (if all |
+ * rewindable closes were undone). If there are no possible rewindable closes this list |
+ * should match {@link #mTabs}. |
+ */ |
+ private List<Tab> mRewoundTabs = new ArrayList<Tab>(); |
+ |
+ @Override |
+ public boolean isIncognito() { |
+ return TabModelBase.this.isIncognito(); |
+ } |
+ |
+ /** |
+ * If {@link TabModel} has a valid selected tab, this will return that same tab in the |
+ * context of the rewound list of tabs. If {@link TabModel} has no tabs but the rewound |
+ * list is not empty, it will return 0, the first tab. Otherwise it will return |
+ * {@link TabModel#INVALID_TAB_INDEX}. |
+ * @return The selected index of the rewound list of tabs (includes all pending closures). |
+ */ |
+ @Override |
+ public int index() { |
+ if (TabModelBase.this.index() != INVALID_TAB_INDEX) { |
+ return mRewoundTabs.indexOf(TabModelUtils.getCurrentTab(TabModelBase.this)); |
+ } |
+ if (!mRewoundTabs.isEmpty()) return 0; |
+ return INVALID_TAB_INDEX; |
+ } |
+ |
+ @Override |
+ public int getCount() { |
+ return mRewoundTabs.size(); |
+ } |
+ |
+ @Override |
+ public Tab getTabAt(int index) { |
+ if (index < 0 || index >= mRewoundTabs.size()) return null; |
+ return mRewoundTabs.get(index); |
+ } |
+ |
+ @Override |
+ public int indexOf(Tab tab) { |
+ return mRewoundTabs.indexOf(tab); |
+ } |
+ |
+ @Override |
+ public boolean isClosurePending(int tabId) { |
+ return TabModelBase.this.isClosurePending(tabId); |
+ } |
+ |
+ /** |
+ * Resets this list to match the original {@link TabModel}. Note that if the |
+ * {@link TabModel} doesn't support pending closures this model will be empty. This should |
+ * be called whenever {@link #mTabs} changes. |
+ */ |
+ public void resetRewoundState() { |
+ mRewoundTabs.clear(); |
+ |
+ if (TabModelBase.this.supportsPendingClosures()) { |
+ for (int i = 0; i < TabModelBase.this.getCount(); i++) { |
+ mRewoundTabs.add(TabModelBase.this.getTabAt(i)); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Finds the {@link Tab} specified by {@code tabId} and only returns it if it is |
+ * actually a {@link Tab} that is in the middle of being closed (which means that it |
+ * is present in this model but not in {@link #mTabs}. |
+ * |
+ * @param tabId The id of the {@link Tab} to search for. |
+ * @return The {@link Tab} specified by {@code tabId} as long as that tab only exists |
+ * in this model and not in {@link #mTabs}. {@code null} otherwise. |
+ */ |
+ public Tab getPendingRewindTab(int tabId) { |
+ if (!TabModelBase.this.supportsPendingClosures()) return null; |
+ if (TabModelUtils.getTabById(TabModelBase.this, tabId) != null) return null; |
+ return TabModelUtils.getTabById(this, tabId); |
+ } |
+ |
+ /** |
+ * A utility method for easily finding a {@link Tab} that can be closed. |
+ * @return The next tab that is in the middle of being closed. |
+ */ |
+ public Tab getNextRewindableTab() { |
+ if (!hasPendingClosures()) return null; |
+ |
+ for (int i = 0; i < mRewoundTabs.size(); i++) { |
+ Tab tab = i < TabModelBase.this.getCount() ? TabModelBase.this.getTabAt(i) : null; |
+ Tab rewoundTab = mRewoundTabs.get(i); |
+ |
+ if (tab == null || rewoundTab.getId() != tab.getId()) return rewoundTab; |
+ } |
+ |
+ return null; |
+ } |
+ |
+ /** |
+ * Removes a {@link Tab} from this internal list. |
+ * @param tab The {@link Tab} to remove. |
+ */ |
+ public void removeTab(Tab tab) { |
+ mRewoundTabs.remove(tab); |
+ } |
+ |
+ /** |
+ * Destroy all tabs in this model. This will check to see if the tab is already destroyed |
+ * before destroying it. |
+ */ |
+ public void destroy() { |
+ for (Tab tab : mRewoundTabs) { |
+ if (tab.isInitialized()) tab.destroy(); |
+ } |
+ } |
+ |
+ public boolean hasPendingClosures() { |
+ return TabModelBase.this.supportsPendingClosures() |
+ && mRewoundTabs.size() > TabModelBase.this.getCount(); |
+ } |
+ } |
+ |
+ /** |
+ * Broadcast a notification (in native code) that all tabs are now loaded from storage. |
+ */ |
+ public void broadcastSessionRestoreComplete() { |
+ nativeBroadcastSessionRestoreComplete(mNativeTabModelImpl); |
+ } |
+ |
+ // JNI related methods ------------------------------------------------------------------------- |
+ |
+ @Override |
+ @CalledByNative |
+ public int getCount() { |
+ return mTabs.size(); |
+ } |
+ |
+ @Override |
+ @CalledByNative |
+ public int index() { |
+ return mIndex; |
+ } |
+ |
+ @SuppressWarnings("unused") |
+ @CalledByNative |
+ private void setIndex(int index) { |
+ TabModelUtils.setIndex(this, index); |
+ } |
+ |
+ /** |
+ * Used by Developer Tools to create a new tab with a given URL. |
+ * |
+ * @param url The URL to open. |
+ * @return The new tab. |
+ */ |
+ @CalledByNative |
+ protected abstract Tab createNewTabForDevTools(String url); |
+ |
+ /** |
+ * Opens the Clear Browsing Data dialog. |
+ */ |
+ @CalledByNative |
+ protected abstract void openClearBrowsingData(); |
+ |
+ @CalledByNative |
+ private boolean isSessionRestoreInProgress() { |
+ return mModelDelegate.isSessionRestoreInProgress(); |
+ } |
+ |
+ private native long nativeInit(boolean isIncognito); |
+ private native void nativeDestroy(long nativeTabModelBase); |
+ private native void nativeBroadcastSessionRestoreComplete(long nativeTabModelBase); |
+ private native Profile nativeGetProfileAndroid(long nativeTabModelBase); |
+ private native void nativeTabAddedToModel(long nativeTabModelBase, Tab tab); |
+} |