blob: a30cdca6c44a8c34ba1348c021f8667501c26eb0 [file] [log] [blame]
The Android Open Source Project0c908882009-03-03 19:32:16 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
The Android Open Source Project0c908882009-03-03 19:32:16 -080018
The Android Open Source Project0c908882009-03-03 19:32:16 -080019import android.os.Bundle;
The Android Open Source Project0c908882009-03-03 19:32:16 -080020import android.util.Log;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080021
Axesh R. Ajmera2e241242014-05-19 15:53:38 -070022import org.codeaurora.swe.GeolocationPermissions;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080023import org.codeaurora.swe.WebView;
Enrico Ros1f5a0952014-11-18 20:15:48 -080024import org.codeaurora.swe.util.Observable;
The Android Open Source Project0c908882009-03-03 19:32:16 -080025
The Android Open Source Project0c908882009-03-03 19:32:16 -080026import java.util.ArrayList;
Elliott Slaughter3d6df162010-08-25 13:17:44 -070027import java.util.HashMap;
Michael Kolb1bf23132010-11-19 12:55:12 -080028import java.util.List;
The Android Open Source Project0c908882009-03-03 19:32:16 -080029import java.util.Vector;
30
31class TabControl {
32 // Log Tag
33 private static final String LOGTAG = "TabControl";
Michael Kolbc831b632011-05-11 09:30:34 -070034
John Reckd8c74522011-06-14 08:45:00 -070035 // next Tab ID, starting at 1
36 private static long sNextId = 1;
Michael Kolbc831b632011-05-11 09:30:34 -070037
38 private static final String POSITIONS = "positions";
39 private static final String CURRENT = "current";
40
John Reck8ee633f2011-08-09 16:00:35 -070041 public static interface OnThumbnailUpdatedListener {
42 void onThumbnailUpdated(Tab t);
43 }
44
The Android Open Source Project0c908882009-03-03 19:32:16 -080045 // Maximum number of tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070046 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080047 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070048 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080049 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070050 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080051 // Current position in mTabs.
52 private int mCurrentTab = -1;
Michael Kolb8233fac2010-10-26 16:08:53 -070053 // the main browser controller
54 private final Controller mController;
Axesh R. Ajmera2e241242014-05-19 15:53:38 -070055 // number of incognito tabs
56 private int mNumIncognito = 0;
Michael Kolb8233fac2010-10-26 16:08:53 -070057
John Reck8ee633f2011-08-09 16:00:35 -070058 private OnThumbnailUpdatedListener mOnThumbnailUpdatedListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080059
Enrico Ros1f5a0952014-11-18 20:15:48 -080060 private Observable mTabCountObservable;
61
The Android Open Source Project0c908882009-03-03 19:32:16 -080062 /**
Michael Kolb8233fac2010-10-26 16:08:53 -070063 * Construct a new TabControl object
The Android Open Source Project0c908882009-03-03 19:32:16 -080064 */
Michael Kolb8233fac2010-10-26 16:08:53 -070065 TabControl(Controller controller) {
66 mController = controller;
Michael Kolb8233fac2010-10-26 16:08:53 -070067 mMaxTabs = mController.getMaxTabs();
Michael Kolb6e4653e2010-09-27 16:22:38 -070068 mTabs = new ArrayList<Tab>(mMaxTabs);
69 mTabQueue = new ArrayList<Tab>(mMaxTabs);
Enrico Ros1f5a0952014-11-18 20:15:48 -080070 mTabCountObservable = new Observable();
71 mTabCountObservable.set(0);
The Android Open Source Project0c908882009-03-03 19:32:16 -080072 }
73
John Reck52be4782011-08-26 15:37:29 -070074 synchronized static long getNextId() {
Michael Kolbc831b632011-05-11 09:30:34 -070075 return sNextId++;
76 }
77
Enrico Ros1f5a0952014-11-18 20:15:48 -080078 Observable getTabCountObservable() {
79 return mTabCountObservable;
80 }
81
Michael Kolb68775752010-08-19 12:42:01 -070082 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080083 * Return the current tab's main WebView. This will always return the main
84 * WebView for a given tab and not a subwindow.
85 * @return The current tab's WebView.
86 */
87 WebView getCurrentWebView() {
88 Tab t = getTab(mCurrentTab);
89 if (t == null) {
90 return null;
91 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070092 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +010093 }
94
95 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080096 * Return the current tab's top-level WebView. This can return a subwindow
97 * if one exists.
98 * @return The top-level WebView of the current tab.
99 */
100 WebView getCurrentTopWebView() {
101 Tab t = getTab(mCurrentTab);
102 if (t == null) {
103 return null;
104 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700105 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800106 }
107
108 /**
109 * Return the current tab's subwindow if it exists.
110 * @return The subwindow of the current tab or null if it doesn't exist.
111 */
112 WebView getCurrentSubWindow() {
113 Tab t = getTab(mCurrentTab);
114 if (t == null) {
115 return null;
116 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700117 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800118 }
119
120 /**
Michael Kolb1bf23132010-11-19 12:55:12 -0800121 * return the list of tabs
122 */
123 List<Tab> getTabs() {
124 return mTabs;
125 }
126
127 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700128 * Return the tab at the specified position.
129 * @return The Tab for the specified position or null if the tab does not
The Android Open Source Project0c908882009-03-03 19:32:16 -0800130 * exist.
131 */
Michael Kolbc831b632011-05-11 09:30:34 -0700132 Tab getTab(int position) {
133 if (position >= 0 && position < mTabs.size()) {
134 return mTabs.get(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800135 }
136 return null;
137 }
138
139 /**
140 * Return the current tab.
141 * @return The current tab.
142 */
143 Tab getCurrentTab() {
144 return getTab(mCurrentTab);
145 }
146
147 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700148 * Return the current tab position.
149 * @return The current tab position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800150 */
Michael Kolbc831b632011-05-11 09:30:34 -0700151 int getCurrentPosition() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800152 return mCurrentTab;
153 }
Michael Kolbfe251992010-07-08 15:41:55 -0700154
The Android Open Source Project0c908882009-03-03 19:32:16 -0800155 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700156 * Given a Tab, find it's position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800157 * @param Tab to find
Michael Kolbc831b632011-05-11 09:30:34 -0700158 * @return position of Tab or -1 if not found
The Android Open Source Project0c908882009-03-03 19:32:16 -0800159 */
Michael Kolbc831b632011-05-11 09:30:34 -0700160 int getTabPosition(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400161 if (tab == null) {
162 return -1;
163 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800164 return mTabs.indexOf(tab);
165 }
166
Grace Kloba22ac16e2009-10-07 18:00:23 -0700167 boolean canCreateNewTab() {
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100168 return mMaxTabs > mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700169 }
170
The Android Open Source Project0c908882009-03-03 19:32:16 -0800171 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700172 * Returns true if there are any incognito tabs open.
173 * @return True when any incognito tabs are open, false otherwise.
174 */
175 boolean hasAnyOpenIncognitoTabs() {
176 for (Tab tab : mTabs) {
Michael Kolbc831b632011-05-11 09:30:34 -0700177 if (tab.getWebView() != null
178 && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700179 return true;
180 }
181 }
182 return false;
183 }
184
Michael Kolb14612442011-06-24 13:06:29 -0700185 void addPreloadedTab(Tab tab) {
Mathew Inwoode09305e2011-09-02 12:03:26 +0100186 for (Tab current : mTabs) {
187 if (current != null && current.getId() == tab.getId()) {
188 throw new IllegalStateException("Tab with id " + tab.getId() + " already exists: "
189 + current.toString());
190 }
191 }
Michael Kolb14612442011-06-24 13:06:29 -0700192 mTabs.add(tab);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800193 mTabCountObservable.set(mTabs.size());
Michael Kolb14612442011-06-24 13:06:29 -0700194 tab.setController(mController);
195 mController.onSetWebView(tab, tab.getWebView());
196 tab.putInBackground();
197 }
198
Elliott Slaughtere440a882010-08-20 13:54:45 -0700199 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700200 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800201 * @return The newly createTab or null if we have reached the maximum
202 * number of open tabs.
203 */
Michael Kolb7bcafde2011-05-09 13:55:59 -0700204 Tab createNewTab(boolean privateBrowsing) {
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500205 return createNewTab(null, privateBrowsing, false);
206 }
207
208 Tab createNewTab(boolean privateBrowsing, boolean backgroundTab) {
209 return createNewTab(null, privateBrowsing, backgroundTab);
John Reck1cf4b792011-07-26 10:22:22 -0700210 }
211
212 Tab createNewTab(Bundle state, boolean privateBrowsing) {
Stewart Chao3f628f72015-01-07 10:03:18 -0500213 return createNewTab(state, privateBrowsing, false);
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500214 }
215
216 Tab createNewTab(Bundle state, boolean privateBrowsing, boolean backgroundTab) {
John Reck1cf4b792011-07-26 10:22:22 -0700217 int size = mTabs.size();
218 // Return false if we have maxed out on tabs
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100219 if (!canCreateNewTab()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800220 return null;
221 }
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100222
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500223 final WebView w = createNewWebView(privateBrowsing, backgroundTab);
Steve Block2bc69912009-07-30 14:45:13 +0100224
The Android Open Source Project0c908882009-03-03 19:32:16 -0800225 // Create a new tab and add it to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700226 Tab t = new Tab(mController, w, state);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800227 mTabs.add(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800228 mTabCountObservable.set(mTabs.size());
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700229 if (privateBrowsing) {
230 mNumIncognito += 1;
231 }
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700232 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700233 t.putInBackground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800234 return t;
235 }
236
237 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700238 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700239 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700240 */
241 Tab createNewTab() {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700242 return createNewTab(false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700243 }
244
Axesh R. Ajmera4b000312015-07-23 10:00:16 -0700245 SnapshotTab createSnapshotTab(long snapshotId, Bundle state) {
246 SnapshotTab t = new SnapshotTab(mController, snapshotId, state);
John Reckd8c74522011-06-14 08:45:00 -0700247 mTabs.add(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800248 mTabCountObservable.set(mTabs.size());
John Reckd8c74522011-06-14 08:45:00 -0700249 return t;
250 }
251
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700252 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500253 * Remove the parent child relationships from all tabs.
254 */
255 void removeParentChildRelationShips() {
256 for (Tab tab : mTabs) {
257 tab.removeFromTree();
258 }
259 }
260
261 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800262 * Remove the tab from the list. If the tab is the current tab shown, the
263 * last created tab will be shown.
264 * @param t The tab to be removed.
265 */
266 boolean removeTab(Tab t) {
267 if (t == null) {
268 return false;
269 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700270
Patrick Scottd944d4d2010-01-27 16:39:11 -0500271 // Grab the current tab before modifying the list.
272 Tab current = getCurrentTab();
273
274 // Remove t from our list of tabs.
275 mTabs.remove(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800276 mTabCountObservable.set(mTabs.size());
Patrick Scottd944d4d2010-01-27 16:39:11 -0500277
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700278 //Clear incognito geolocation state if this is the last incognito tab.
279 if (t.isPrivateBrowsingEnabled()) {
280 mNumIncognito -= 1;
281 if (mNumIncognito == 0) {
282 GeolocationPermissions.onIncognitoTabsRemoved();
283 }
284 }
285
Patrick Scottd944d4d2010-01-27 16:39:11 -0500286 // Put the tab in the background only if it is the current one.
287 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700288 t.putInBackground();
289 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500290 } else {
291 // If a tab that is earlier in the list gets removed, the current
292 // index no longer points to the correct tab.
Michael Kolbc831b632011-05-11 09:30:34 -0700293 mCurrentTab = getTabPosition(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800294 }
295
Grace Kloba22ac16e2009-10-07 18:00:23 -0700296 // destroy the tab
297 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800298 // clear it's references to parent and children
299 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800300
The Android Open Source Project0c908882009-03-03 19:32:16 -0800301 // Remove it from the queue of viewed tabs.
302 mTabQueue.remove(t);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800303 return true;
304 }
305
306 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800307 * Destroy all the tabs and subwindows
308 */
309 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800310 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700311 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800312 }
313 mTabs.clear();
314 mTabQueue.clear();
315 }
316
317 /**
318 * Returns the number of tabs created.
319 * @return The number of tabs created.
320 */
321 int getTabCount() {
322 return mTabs.size();
323 }
324
The Android Open Source Project0c908882009-03-03 19:32:16 -0800325 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700326 * save the tab state:
327 * current position
328 * position sorted array of tab ids
329 * for each tab id, save the tab state
330 * @param outState
John Reckaed9c542011-05-27 16:08:53 -0700331 * @param saveImages
The Android Open Source Project0c908882009-03-03 19:32:16 -0800332 */
John Reck1cf4b792011-07-26 10:22:22 -0700333 void saveState(Bundle outState) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800334 final int numTabs = getTabCount();
John Reck24f18262011-06-17 14:47:20 -0700335 if (numTabs == 0) {
336 return;
337 }
Michael Kolbc831b632011-05-11 09:30:34 -0700338 long[] ids = new long[numTabs];
339 int i = 0;
340 for (Tab tab : mTabs) {
John Reck1cf4b792011-07-26 10:22:22 -0700341 Bundle tabState = tab.saveState();
Tarun Nainani8eb00912014-07-17 12:28:32 -0700342 if (tabState != null && tab.isPrivateBrowsingEnabled() == false) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700343 ids[i++] = tab.getId();
John Reck52be4782011-08-26 15:37:29 -0700344 String key = Long.toString(tab.getId());
345 if (outState.containsKey(key)) {
346 // Dump the tab state for debugging purposes
347 for (Tab dt : mTabs) {
348 Log.e(LOGTAG, dt.toString());
349 }
350 throw new IllegalStateException(
351 "Error saving state, duplicate tab ids!");
352 }
353 outState.putBundle(key, tabState);
Michael Kolb3ae7f742011-07-13 15:18:25 -0700354 } else {
355 ids[i++] = -1;
John Reck52be4782011-08-26 15:37:29 -0700356 // Since we won't be restoring the thumbnail, delete it
357 tab.deleteThumbnail();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800358 }
359 }
John Reck24f18262011-06-17 14:47:20 -0700360 if (!outState.isEmpty()) {
361 outState.putLongArray(POSITIONS, ids);
362 Tab current = getCurrentTab();
363 long cid = -1;
364 if (current != null) {
365 cid = current.getId();
366 }
367 outState.putLong(CURRENT, cid);
Michael Kolbdbf39812011-06-10 14:06:40 -0700368 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800369 }
370
371 /**
Patrick Scott7d50a932011-02-04 09:27:26 -0500372 * Check if the state can be restored. If the state can be restored, the
Michael Kolbc831b632011-05-11 09:30:34 -0700373 * current tab id is returned. This can be passed to restoreState below
Patrick Scott7d50a932011-02-04 09:27:26 -0500374 * in order to restore the correct tab. Otherwise, -1 is returned and the
375 * state cannot be restored.
376 */
Michael Kolbc831b632011-05-11 09:30:34 -0700377 long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
378 final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
379 if (ids == null) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500380 return -1;
381 }
Michael Kolbc831b632011-05-11 09:30:34 -0700382 final long oldcurrent = inState.getLong(CURRENT);
383 long current = -1;
Michael Kolb3ae7f742011-07-13 15:18:25 -0700384 if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
John Reck1cf4b792011-07-26 10:22:22 -0700385 current = oldcurrent;
Patrick Scott7d50a932011-02-04 09:27:26 -0500386 } else {
Michael Kolbc831b632011-05-11 09:30:34 -0700387 // pick first non incognito tab
388 for (long id : ids) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700389 if (hasState(id, inState) && !isIncognito(id, inState)) {
Michael Kolbc831b632011-05-11 09:30:34 -0700390 current = id;
Patrick Scott7d50a932011-02-04 09:27:26 -0500391 break;
392 }
393 }
394 }
Michael Kolbc831b632011-05-11 09:30:34 -0700395 return current;
Patrick Scott7d50a932011-02-04 09:27:26 -0500396 }
397
Michael Kolb3ae7f742011-07-13 15:18:25 -0700398 private boolean hasState(long id, Bundle state) {
399 if (id == -1) return false;
400 Bundle tab = state.getBundle(Long.toString(id));
401 return ((tab != null) && !tab.isEmpty());
402 }
403
404 private boolean isIncognito(long id, Bundle state) {
405 Bundle tabstate = state.getBundle(Long.toString(id));
406 if ((tabstate != null) && !tabstate.isEmpty()) {
407 return tabstate.getBoolean(Tab.INCOGNITO);
408 }
409 return false;
410 }
411
Patrick Scott7d50a932011-02-04 09:27:26 -0500412 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800413 * Restore the state of all the tabs.
Michael Kolbc831b632011-05-11 09:30:34 -0700414 * @param currentId The tab id to restore.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800415 * @param inState The saved state of all the tabs.
Michael Kolb1bf23132010-11-19 12:55:12 -0800416 * @param restoreIncognitoTabs Restoring private browsing tabs
417 * @param restoreAll All webviews get restored, not just the current tab
418 * (this does not override handling of incognito tabs)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800419 */
Michael Kolbc831b632011-05-11 09:30:34 -0700420 void restoreState(Bundle inState, long currentId,
Patrick Scott7d50a932011-02-04 09:27:26 -0500421 boolean restoreIncognitoTabs, boolean restoreAll) {
Michael Kolbc831b632011-05-11 09:30:34 -0700422 if (currentId == -1) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500423 return;
424 }
Michael Kolbc831b632011-05-11 09:30:34 -0700425 long[] ids = inState.getLongArray(POSITIONS);
426 long maxId = -Long.MAX_VALUE;
427 HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>();
428 for (long id : ids) {
429 if (id > maxId) {
430 maxId = id;
431 }
432 final String idkey = Long.toString(id);
433 Bundle state = inState.getBundle(idkey);
John Reckd8c74522011-06-14 08:45:00 -0700434 if (state == null || state.isEmpty()) {
435 // Skip tab
436 continue;
437 } else if (!restoreIncognitoTabs
Michael Kolbc831b632011-05-11 09:30:34 -0700438 && state.getBoolean(Tab.INCOGNITO)) {
439 // ignore tab
440 } else if (id == currentId || restoreAll) {
Axesh R. Ajmerae6b11aa2015-03-16 18:20:41 -0700441 Tab t = null;
442 // Add special check to restore Snapshot Tab if needed
443 if (state.getLong(SnapshotTab.SNAPSHOT_ID, -1) != -1 ) {
Axesh R. Ajmera4b000312015-07-23 10:00:16 -0700444 t = (SnapshotTab) createSnapshotTab( state.getLong(SnapshotTab.SNAPSHOT_ID), state);
Axesh R. Ajmerae6b11aa2015-03-16 18:20:41 -0700445 } else {
446 // presume its a normal Tab
447 t = createNewTab(state, false);
448 }
449
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100450 if (t == null) {
451 // We could "break" at this point, but we want
452 // sNextId to be set correctly.
453 continue;
454 }
Michael Kolbc831b632011-05-11 09:30:34 -0700455 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500456 // Me must set the current tab before restoring the state
457 // so that all the client classes are set.
Michael Kolbc831b632011-05-11 09:30:34 -0700458 if (id == currentId) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500459 setCurrentTab(t);
460 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700461 } else {
Patrick Scott7d50a932011-02-04 09:27:26 -0500462 // Create a new tab and don't restore the state yet, add it
463 // to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700464 Tab t = new Tab(mController, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700465 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500466 mTabs.add(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800467 mTabCountObservable.set(mTabs.size());
Patrick Scott7d50a932011-02-04 09:27:26 -0500468 // added the tab to the front as they are not current
469 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700470 }
Michael Kolbc831b632011-05-11 09:30:34 -0700471 }
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100472
473 // make sure that there is no id overlap between the restored
474 // and new tabs
475 sNextId = maxId + 1;
476
John Reckd8c74522011-06-14 08:45:00 -0700477 if (mCurrentTab == -1) {
478 if (getTabCount() > 0) {
479 setCurrentTab(getTab(0));
John Reckd8c74522011-06-14 08:45:00 -0700480 }
481 }
Michael Kolbc831b632011-05-11 09:30:34 -0700482 // restore parent/child relationships
483 for (long id : ids) {
484 final Tab tab = tabMap.get(id);
485 final Bundle b = inState.getBundle(Long.toString(id));
486 if ((b != null) && (tab != null)) {
487 final long parentId = b.getLong(Tab.PARENTTAB, -1);
488 if (parentId != -1) {
489 final Tab parent = tabMap.get(parentId);
Patrick Scott7d50a932011-02-04 09:27:26 -0500490 if (parent != null) {
Michael Kolbc831b632011-05-11 09:30:34 -0700491 parent.addChildTab(tab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800492 }
493 }
494 }
495 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800496 }
497
498 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700499 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800500 * WebView cache;
501 */
502 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700503 if (getTabCount() == 0) return;
504
Grace Klobaf56f68d2010-04-11 22:53:42 -0700505 // free the least frequently used background tabs
506 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
507 if (tabs.size() > 0) {
508 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
509 for (Tab t : tabs) {
510 // store the WebView's state.
511 t.saveState();
512 // destroy the tab
513 t.destroy();
514 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800515 return;
516 }
517
Derek Sollenberger4433d032009-06-10 15:37:21 -0400518 // free the WebView's unused memory (this includes the cache)
519 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800520 WebView view = getCurrentWebView();
521 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400522 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800523 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800524 }
525
Grace Klobaf56f68d2010-04-11 22:53:42 -0700526 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
527 Vector<Tab> tabsToGo = new Vector<Tab>();
528
Patrick Scott2a67de42009-08-31 09:48:37 -0400529 // Don't do anything if we only have 1 tab or if the current tab is
530 // null.
531 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700532 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800533 }
534
Grace Klobaf56f68d2010-04-11 22:53:42 -0700535 if (mTabQueue.size() == 0) {
536 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800537 }
538
Grace Klobaf56f68d2010-04-11 22:53:42 -0700539 // Rip through the queue starting at the beginning and tear down half of
540 // available tabs which are not the current tab or the parent of the
541 // current tab.
542 int openTabCount = 0;
543 for (Tab t : mTabQueue) {
544 if (t != null && t.getWebView() != null) {
545 openTabCount++;
Michael Kolbc831b632011-05-11 09:30:34 -0700546 if (t != current && t != current.getParent()) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700547 tabsToGo.add(t);
548 }
549 }
550 }
551
552 openTabCount /= 2;
553 if (tabsToGo.size() > openTabCount) {
554 tabsToGo.setSize(openTabCount);
555 }
556
557 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800558 }
559
Michael Kolbff6a7482011-07-26 16:37:15 -0700560 Tab getLeastUsedTab(Tab current) {
561 if (getTabCount() == 1 || current == null) {
562 return null;
563 }
564 if (mTabQueue.size() == 0) {
565 return null;
566 }
567 // find a tab which is not the current tab or the parent of the
568 // current tab
569 for (Tab t : mTabQueue) {
570 if (t != null && t.getWebView() != null) {
571 if (t != current && t != current.getParent()) {
572 return t;
573 }
574 }
575 }
576 return null;
577 }
578
The Android Open Source Project0c908882009-03-03 19:32:16 -0800579 /**
580 * Show the tab that contains the given WebView.
581 * @param view The WebView used to find the tab.
582 */
583 Tab getTabFromView(WebView view) {
Michael Kolbc831b632011-05-11 09:30:34 -0700584 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700585 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800586 return t;
587 }
588 }
589 return null;
590 }
591
592 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700593 * Return the tab with the matching application id.
594 * @param id The application identifier.
595 */
Michael Kolbc831b632011-05-11 09:30:34 -0700596 Tab getTabFromAppId(String id) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700597 if (id == null) {
598 return null;
599 }
Michael Kolbc831b632011-05-11 09:30:34 -0700600 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700601 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700602 return t;
603 }
604 }
605 return null;
606 }
607
Grace Kloba22ac16e2009-10-07 18:00:23 -0700608 /**
609 * Stop loading in all opened WebView including subWindows.
610 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700611 void stopAllLoading() {
Michael Kolbc831b632011-05-11 09:30:34 -0700612 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700613 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700614 if (webview != null) {
615 webview.stopLoading();
616 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700617 final WebView subview = t.getSubWebView();
618 if (subview != null) {
Mattias Nilsson9727af82012-06-14 17:07:56 +0200619 subview.stopLoading();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700620 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700621 }
622 }
623
John Reckdb22ec42011-06-29 11:31:24 -0700624 // This method checks if a tab matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400625 private boolean tabMatchesUrl(Tab t, String url) {
John Reckdb22ec42011-06-29 11:31:24 -0700626 return url.equals(t.getUrl()) || url.equals(t.getOriginalUrl());
Patrick Scottcd115892009-07-16 09:42:58 -0400627 }
628
629 /**
John Reckdb22ec42011-06-29 11:31:24 -0700630 * Return the tab that matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400631 * @param url The url to search for.
632 */
John Reckdb22ec42011-06-29 11:31:24 -0700633 Tab findTabWithUrl(String url) {
Patrick Scottcd115892009-07-16 09:42:58 -0400634 if (url == null) {
635 return null;
636 }
637 // Check the current tab first.
John Reckdb22ec42011-06-29 11:31:24 -0700638 Tab currentTab = getCurrentTab();
639 if (currentTab != null && tabMatchesUrl(currentTab, url)) {
640 return currentTab;
Patrick Scottcd115892009-07-16 09:42:58 -0400641 }
642 // Now check all the rest.
Michael Kolbc831b632011-05-11 09:30:34 -0700643 for (Tab tab : mTabs) {
644 if (tabMatchesUrl(tab, url)) {
John Reckdb22ec42011-06-29 11:31:24 -0700645 return tab;
Patrick Scottcd115892009-07-16 09:42:58 -0400646 }
647 }
648 return null;
649 }
650
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700651 /**
John Reck30c714c2010-12-16 17:30:34 -0800652 * Recreate the main WebView of the given tab.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700653 */
John Reck30c714c2010-12-16 17:30:34 -0800654 void recreateWebView(Tab t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700655 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700656 if (w != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700657 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700658 }
659 // Create a new WebView. If this tab is the current tab, we need to put
660 // back all the clients so force it to be the current tab.
Panos Thomasa9a5a582014-03-18 19:20:08 -0700661 t.setWebView(createNewWebView(t.isPrivateBrowsingEnabled()), false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700662 if (getCurrentTab() == t) {
663 setCurrentTab(t, true);
664 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700665 }
666
667 /**
668 * Creates a new WebView and registers it with the global settings.
669 */
670 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700671 return createNewWebView(false);
672 }
673
674 /**
675 * Creates a new WebView and registers it with the global settings.
676 * @param privateBrowsing When true, enables private browsing in the new
677 * WebView.
678 */
679 private WebView createNewWebView(boolean privateBrowsing) {
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500680 return createNewWebView(privateBrowsing, false);
681 }
682
683 private WebView createNewWebView(boolean privateBrowsing, boolean backgroundTab) {
684 return mController.getWebViewFactory().createWebView(privateBrowsing, backgroundTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700685 }
686
687 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800688 * Put the current tab in the background and set newTab as the current tab.
689 * @param newTab The new tab. If newTab is null, the current tab is not
690 * set.
691 */
692 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700693 return setCurrentTab(newTab, false);
694 }
695
696 /**
697 * If force is true, this method skips the check for newTab == current.
698 */
699 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800700 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700701 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800702 return true;
703 }
704 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700705 current.putInBackground();
706 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800707 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800708 if (newTab == null) {
709 return false;
710 }
711
712 // Move the newTab to the end of the queue
713 int index = mTabQueue.indexOf(newTab);
714 if (index != -1) {
715 mTabQueue.remove(index);
716 }
717 mTabQueue.add(newTab);
718
The Android Open Source Project0c908882009-03-03 19:32:16 -0800719 // Display the new current tab
720 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700721 WebView mainView = newTab.getWebView();
John Reckd8c74522011-06-14 08:45:00 -0700722 boolean needRestore = !newTab.isSnapshot() && (mainView == null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800723 if (needRestore) {
724 // Same work as in createNewTab() except don't do new Tab()
Panos Thomasa9a5a582014-03-18 19:20:08 -0700725 mainView = createNewWebView(newTab.isPrivateBrowsingEnabled());
Steve Block2bc69912009-07-30 14:45:13 +0100726 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800727 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700728 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800729 return true;
730 }
Michael Kolbfe251992010-07-08 15:41:55 -0700731
John Reck8ee633f2011-08-09 16:00:35 -0700732 public void setOnThumbnailUpdatedListener(OnThumbnailUpdatedListener listener) {
733 mOnThumbnailUpdatedListener = listener;
734 for (Tab t : mTabs) {
735 WebView web = t.getWebView();
736 if (web != null) {
737 web.setPictureListener(listener != null ? t : null);
738 }
739 }
740 }
741
742 public OnThumbnailUpdatedListener getOnThumbnailUpdatedListener() {
743 return mOnThumbnailUpdatedListener;
744 }
745
The Android Open Source Project0c908882009-03-03 19:32:16 -0800746}