blob: e99071a6232b7a7016e7e69ed8bea5c82e1ef0aa [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
17package com.android.browser;
18
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;
The Android Open Source Project0c908882009-03-03 19:32:16 -080021import android.webkit.WebView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080022
The Android Open Source Project0c908882009-03-03 19:32:16 -080023import java.util.ArrayList;
Elliott Slaughter3d6df162010-08-25 13:17:44 -070024import java.util.HashMap;
Michael Kolb1bf23132010-11-19 12:55:12 -080025import java.util.List;
The Android Open Source Project0c908882009-03-03 19:32:16 -080026import java.util.Vector;
27
28class TabControl {
29 // Log Tag
30 private static final String LOGTAG = "TabControl";
Michael Kolbc831b632011-05-11 09:30:34 -070031
John Reckd8c74522011-06-14 08:45:00 -070032 // next Tab ID, starting at 1
33 private static long sNextId = 1;
Michael Kolbc831b632011-05-11 09:30:34 -070034
35 private static final String POSITIONS = "positions";
36 private static final String CURRENT = "current";
37
John Reck8ee633f2011-08-09 16:00:35 -070038 public static interface OnThumbnailUpdatedListener {
39 void onThumbnailUpdated(Tab t);
40 }
41
The Android Open Source Project0c908882009-03-03 19:32:16 -080042 // Maximum number of tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070043 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080044 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070045 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080046 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070047 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080048 // Current position in mTabs.
49 private int mCurrentTab = -1;
Michael Kolb8233fac2010-10-26 16:08:53 -070050 // the main browser controller
51 private final Controller mController;
52
John Reck8ee633f2011-08-09 16:00:35 -070053 private OnThumbnailUpdatedListener mOnThumbnailUpdatedListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080054
55 /**
Michael Kolb8233fac2010-10-26 16:08:53 -070056 * Construct a new TabControl object
The Android Open Source Project0c908882009-03-03 19:32:16 -080057 */
Michael Kolb8233fac2010-10-26 16:08:53 -070058 TabControl(Controller controller) {
59 mController = controller;
Michael Kolb8233fac2010-10-26 16:08:53 -070060 mMaxTabs = mController.getMaxTabs();
Michael Kolb6e4653e2010-09-27 16:22:38 -070061 mTabs = new ArrayList<Tab>(mMaxTabs);
62 mTabQueue = new ArrayList<Tab>(mMaxTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -080063 }
64
Michael Kolbc831b632011-05-11 09:30:34 -070065 static long getNextId() {
66 return sNextId++;
67 }
68
Michael Kolb68775752010-08-19 12:42:01 -070069 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080070 * Return the current tab's main WebView. This will always return the main
71 * WebView for a given tab and not a subwindow.
72 * @return The current tab's WebView.
73 */
74 WebView getCurrentWebView() {
75 Tab t = getTab(mCurrentTab);
76 if (t == null) {
77 return null;
78 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070079 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +010080 }
81
82 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080083 * Return the current tab's top-level WebView. This can return a subwindow
84 * if one exists.
85 * @return The top-level WebView of the current tab.
86 */
87 WebView getCurrentTopWebView() {
88 Tab t = getTab(mCurrentTab);
89 if (t == null) {
90 return null;
91 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070092 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -080093 }
94
95 /**
96 * Return the current tab's subwindow if it exists.
97 * @return The subwindow of the current tab or null if it doesn't exist.
98 */
99 WebView getCurrentSubWindow() {
100 Tab t = getTab(mCurrentTab);
101 if (t == null) {
102 return null;
103 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700104 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800105 }
106
107 /**
Michael Kolb1bf23132010-11-19 12:55:12 -0800108 * return the list of tabs
109 */
110 List<Tab> getTabs() {
111 return mTabs;
112 }
113
114 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700115 * Return the tab at the specified position.
116 * @return The Tab for the specified position or null if the tab does not
The Android Open Source Project0c908882009-03-03 19:32:16 -0800117 * exist.
118 */
Michael Kolbc831b632011-05-11 09:30:34 -0700119 Tab getTab(int position) {
120 if (position >= 0 && position < mTabs.size()) {
121 return mTabs.get(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800122 }
123 return null;
124 }
125
126 /**
127 * Return the current tab.
128 * @return The current tab.
129 */
130 Tab getCurrentTab() {
131 return getTab(mCurrentTab);
132 }
133
134 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700135 * Return the current tab position.
136 * @return The current tab position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800137 */
Michael Kolbc831b632011-05-11 09:30:34 -0700138 int getCurrentPosition() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800139 return mCurrentTab;
140 }
Michael Kolbfe251992010-07-08 15:41:55 -0700141
The Android Open Source Project0c908882009-03-03 19:32:16 -0800142 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700143 * Given a Tab, find it's position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800144 * @param Tab to find
Michael Kolbc831b632011-05-11 09:30:34 -0700145 * @return position of Tab or -1 if not found
The Android Open Source Project0c908882009-03-03 19:32:16 -0800146 */
Michael Kolbc831b632011-05-11 09:30:34 -0700147 int getTabPosition(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400148 if (tab == null) {
149 return -1;
150 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800151 return mTabs.indexOf(tab);
152 }
153
Grace Kloba22ac16e2009-10-07 18:00:23 -0700154 boolean canCreateNewTab() {
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100155 return mMaxTabs > mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700156 }
157
The Android Open Source Project0c908882009-03-03 19:32:16 -0800158 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700159 * Returns true if there are any incognito tabs open.
160 * @return True when any incognito tabs are open, false otherwise.
161 */
162 boolean hasAnyOpenIncognitoTabs() {
163 for (Tab tab : mTabs) {
Michael Kolbc831b632011-05-11 09:30:34 -0700164 if (tab.getWebView() != null
165 && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700166 return true;
167 }
168 }
169 return false;
170 }
171
Michael Kolb14612442011-06-24 13:06:29 -0700172 void addPreloadedTab(Tab tab) {
173 tab.setId(getNextId());
174 mTabs.add(tab);
175 tab.setController(mController);
176 mController.onSetWebView(tab, tab.getWebView());
177 tab.putInBackground();
178 }
179
Elliott Slaughtere440a882010-08-20 13:54:45 -0700180 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700181 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800182 * @return The newly createTab or null if we have reached the maximum
183 * number of open tabs.
184 */
Michael Kolb7bcafde2011-05-09 13:55:59 -0700185 Tab createNewTab(boolean privateBrowsing) {
John Reck1cf4b792011-07-26 10:22:22 -0700186 return createNewTab(null, privateBrowsing);
187 }
188
189 Tab createNewTab(Bundle state, boolean privateBrowsing) {
190 int size = mTabs.size();
191 // Return false if we have maxed out on tabs
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100192 if (!canCreateNewTab()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800193 return null;
194 }
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100195
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700196 final WebView w = createNewWebView(privateBrowsing);
Steve Block2bc69912009-07-30 14:45:13 +0100197
The Android Open Source Project0c908882009-03-03 19:32:16 -0800198 // Create a new tab and add it to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700199 Tab t = new Tab(mController, w, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700200 t.setId(getNextId());
The Android Open Source Project0c908882009-03-03 19:32:16 -0800201 mTabs.add(t);
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700202 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700203 t.putInBackground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800204 return t;
205 }
206
207 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700208 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700209 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700210 */
211 Tab createNewTab() {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700212 return createNewTab(false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700213 }
214
John Reckd8c74522011-06-14 08:45:00 -0700215 SnapshotTab createSnapshotTab(long snapshotId) {
John Reckd8c74522011-06-14 08:45:00 -0700216 SnapshotTab t = new SnapshotTab(mController, snapshotId);
217 t.setId(getNextId());
218 mTabs.add(t);
219 return t;
220 }
221
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700222 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500223 * Remove the parent child relationships from all tabs.
224 */
225 void removeParentChildRelationShips() {
226 for (Tab tab : mTabs) {
227 tab.removeFromTree();
228 }
229 }
230
231 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800232 * Remove the tab from the list. If the tab is the current tab shown, the
233 * last created tab will be shown.
234 * @param t The tab to be removed.
235 */
236 boolean removeTab(Tab t) {
237 if (t == null) {
238 return false;
239 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700240
Patrick Scottd944d4d2010-01-27 16:39:11 -0500241 // Grab the current tab before modifying the list.
242 Tab current = getCurrentTab();
243
244 // Remove t from our list of tabs.
245 mTabs.remove(t);
246
247 // Put the tab in the background only if it is the current one.
248 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700249 t.putInBackground();
250 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500251 } else {
252 // If a tab that is earlier in the list gets removed, the current
253 // index no longer points to the correct tab.
Michael Kolbc831b632011-05-11 09:30:34 -0700254 mCurrentTab = getTabPosition(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800255 }
256
Grace Kloba22ac16e2009-10-07 18:00:23 -0700257 // destroy the tab
258 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800259 // clear it's references to parent and children
260 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800261
The Android Open Source Project0c908882009-03-03 19:32:16 -0800262 // Remove it from the queue of viewed tabs.
263 mTabQueue.remove(t);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800264 return true;
265 }
266
267 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800268 * Destroy all the tabs and subwindows
269 */
270 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800271 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700272 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800273 }
274 mTabs.clear();
275 mTabQueue.clear();
276 }
277
278 /**
279 * Returns the number of tabs created.
280 * @return The number of tabs created.
281 */
282 int getTabCount() {
283 return mTabs.size();
284 }
285
The Android Open Source Project0c908882009-03-03 19:32:16 -0800286 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700287 * save the tab state:
288 * current position
289 * position sorted array of tab ids
290 * for each tab id, save the tab state
291 * @param outState
John Reckaed9c542011-05-27 16:08:53 -0700292 * @param saveImages
The Android Open Source Project0c908882009-03-03 19:32:16 -0800293 */
John Reck1cf4b792011-07-26 10:22:22 -0700294 void saveState(Bundle outState) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800295 final int numTabs = getTabCount();
John Reck24f18262011-06-17 14:47:20 -0700296 if (numTabs == 0) {
297 return;
298 }
Michael Kolbc831b632011-05-11 09:30:34 -0700299 long[] ids = new long[numTabs];
300 int i = 0;
301 for (Tab tab : mTabs) {
John Reck1cf4b792011-07-26 10:22:22 -0700302 Bundle tabState = tab.saveState();
303 if (tabState != null) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700304 ids[i++] = tab.getId();
John Reck1cf4b792011-07-26 10:22:22 -0700305 outState.putBundle(Long.toString(tab.getId()), tabState);
Michael Kolb3ae7f742011-07-13 15:18:25 -0700306 } else {
307 ids[i++] = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800308 }
309 }
John Reck24f18262011-06-17 14:47:20 -0700310 if (!outState.isEmpty()) {
311 outState.putLongArray(POSITIONS, ids);
312 Tab current = getCurrentTab();
313 long cid = -1;
314 if (current != null) {
315 cid = current.getId();
316 }
317 outState.putLong(CURRENT, cid);
Michael Kolbdbf39812011-06-10 14:06:40 -0700318 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800319 }
320
321 /**
Patrick Scott7d50a932011-02-04 09:27:26 -0500322 * Check if the state can be restored. If the state can be restored, the
Michael Kolbc831b632011-05-11 09:30:34 -0700323 * current tab id is returned. This can be passed to restoreState below
Patrick Scott7d50a932011-02-04 09:27:26 -0500324 * in order to restore the correct tab. Otherwise, -1 is returned and the
325 * state cannot be restored.
326 */
Michael Kolbc831b632011-05-11 09:30:34 -0700327 long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
328 final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
329 if (ids == null) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500330 return -1;
331 }
Michael Kolbc831b632011-05-11 09:30:34 -0700332 final long oldcurrent = inState.getLong(CURRENT);
333 long current = -1;
Michael Kolb3ae7f742011-07-13 15:18:25 -0700334 if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
John Reck1cf4b792011-07-26 10:22:22 -0700335 current = oldcurrent;
Patrick Scott7d50a932011-02-04 09:27:26 -0500336 } else {
Michael Kolbc831b632011-05-11 09:30:34 -0700337 // pick first non incognito tab
338 for (long id : ids) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700339 if (hasState(id, inState) && !isIncognito(id, inState)) {
Michael Kolbc831b632011-05-11 09:30:34 -0700340 current = id;
Patrick Scott7d50a932011-02-04 09:27:26 -0500341 break;
342 }
343 }
344 }
Michael Kolbc831b632011-05-11 09:30:34 -0700345 return current;
Patrick Scott7d50a932011-02-04 09:27:26 -0500346 }
347
Michael Kolb3ae7f742011-07-13 15:18:25 -0700348 private boolean hasState(long id, Bundle state) {
349 if (id == -1) return false;
350 Bundle tab = state.getBundle(Long.toString(id));
351 return ((tab != null) && !tab.isEmpty());
352 }
353
354 private boolean isIncognito(long id, Bundle state) {
355 Bundle tabstate = state.getBundle(Long.toString(id));
356 if ((tabstate != null) && !tabstate.isEmpty()) {
357 return tabstate.getBoolean(Tab.INCOGNITO);
358 }
359 return false;
360 }
361
Patrick Scott7d50a932011-02-04 09:27:26 -0500362 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800363 * Restore the state of all the tabs.
Michael Kolbc831b632011-05-11 09:30:34 -0700364 * @param currentId The tab id to restore.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800365 * @param inState The saved state of all the tabs.
Michael Kolb1bf23132010-11-19 12:55:12 -0800366 * @param restoreIncognitoTabs Restoring private browsing tabs
367 * @param restoreAll All webviews get restored, not just the current tab
368 * (this does not override handling of incognito tabs)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800369 */
Michael Kolbc831b632011-05-11 09:30:34 -0700370 void restoreState(Bundle inState, long currentId,
Patrick Scott7d50a932011-02-04 09:27:26 -0500371 boolean restoreIncognitoTabs, boolean restoreAll) {
Michael Kolbc831b632011-05-11 09:30:34 -0700372 if (currentId == -1) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500373 return;
374 }
Michael Kolbc831b632011-05-11 09:30:34 -0700375 long[] ids = inState.getLongArray(POSITIONS);
376 long maxId = -Long.MAX_VALUE;
377 HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>();
378 for (long id : ids) {
379 if (id > maxId) {
380 maxId = id;
381 }
382 final String idkey = Long.toString(id);
383 Bundle state = inState.getBundle(idkey);
John Reckd8c74522011-06-14 08:45:00 -0700384 if (state == null || state.isEmpty()) {
385 // Skip tab
386 continue;
387 } else if (!restoreIncognitoTabs
Michael Kolbc831b632011-05-11 09:30:34 -0700388 && state.getBoolean(Tab.INCOGNITO)) {
389 // ignore tab
390 } else if (id == currentId || restoreAll) {
John Reck1cf4b792011-07-26 10:22:22 -0700391 Tab t = createNewTab(state, false);
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100392 if (t == null) {
393 // We could "break" at this point, but we want
394 // sNextId to be set correctly.
395 continue;
396 }
Michael Kolbc831b632011-05-11 09:30:34 -0700397 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500398 // Me must set the current tab before restoring the state
399 // so that all the client classes are set.
Michael Kolbc831b632011-05-11 09:30:34 -0700400 if (id == currentId) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500401 setCurrentTab(t);
402 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700403 } else {
Patrick Scott7d50a932011-02-04 09:27:26 -0500404 // Create a new tab and don't restore the state yet, add it
405 // to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700406 Tab t = new Tab(mController, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700407 t.setId(id);
408 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500409 mTabs.add(t);
410 // added the tab to the front as they are not current
411 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700412 }
Michael Kolbc831b632011-05-11 09:30:34 -0700413 }
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100414
415 // make sure that there is no id overlap between the restored
416 // and new tabs
417 sNextId = maxId + 1;
418
John Reckd8c74522011-06-14 08:45:00 -0700419 if (mCurrentTab == -1) {
420 if (getTabCount() > 0) {
421 setCurrentTab(getTab(0));
422 } else {
423 Tab t = createNewTab();
424 setCurrentTab(t);
425 t.getWebView().loadUrl(BrowserSettings.getInstance()
426 .getHomePage());
427 }
428 }
Michael Kolbc831b632011-05-11 09:30:34 -0700429 // restore parent/child relationships
430 for (long id : ids) {
431 final Tab tab = tabMap.get(id);
432 final Bundle b = inState.getBundle(Long.toString(id));
433 if ((b != null) && (tab != null)) {
434 final long parentId = b.getLong(Tab.PARENTTAB, -1);
435 if (parentId != -1) {
436 final Tab parent = tabMap.get(parentId);
Patrick Scott7d50a932011-02-04 09:27:26 -0500437 if (parent != null) {
Michael Kolbc831b632011-05-11 09:30:34 -0700438 parent.addChildTab(tab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800439 }
440 }
441 }
442 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800443 }
444
445 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700446 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800447 * WebView cache;
448 */
449 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700450 if (getTabCount() == 0) return;
451
Grace Klobaf56f68d2010-04-11 22:53:42 -0700452 // free the least frequently used background tabs
453 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
454 if (tabs.size() > 0) {
455 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
456 for (Tab t : tabs) {
457 // store the WebView's state.
458 t.saveState();
459 // destroy the tab
460 t.destroy();
461 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800462 return;
463 }
464
Derek Sollenberger4433d032009-06-10 15:37:21 -0400465 // free the WebView's unused memory (this includes the cache)
466 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800467 WebView view = getCurrentWebView();
468 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400469 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800470 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800471 }
472
Grace Klobaf56f68d2010-04-11 22:53:42 -0700473 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
474 Vector<Tab> tabsToGo = new Vector<Tab>();
475
Patrick Scott2a67de42009-08-31 09:48:37 -0400476 // Don't do anything if we only have 1 tab or if the current tab is
477 // null.
478 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700479 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800480 }
481
Grace Klobaf56f68d2010-04-11 22:53:42 -0700482 if (mTabQueue.size() == 0) {
483 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800484 }
485
Grace Klobaf56f68d2010-04-11 22:53:42 -0700486 // Rip through the queue starting at the beginning and tear down half of
487 // available tabs which are not the current tab or the parent of the
488 // current tab.
489 int openTabCount = 0;
490 for (Tab t : mTabQueue) {
491 if (t != null && t.getWebView() != null) {
492 openTabCount++;
Michael Kolbc831b632011-05-11 09:30:34 -0700493 if (t != current && t != current.getParent()) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700494 tabsToGo.add(t);
495 }
496 }
497 }
498
499 openTabCount /= 2;
500 if (tabsToGo.size() > openTabCount) {
501 tabsToGo.setSize(openTabCount);
502 }
503
504 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800505 }
506
Michael Kolbff6a7482011-07-26 16:37:15 -0700507 Tab getLeastUsedTab(Tab current) {
508 if (getTabCount() == 1 || current == null) {
509 return null;
510 }
511 if (mTabQueue.size() == 0) {
512 return null;
513 }
514 // find a tab which is not the current tab or the parent of the
515 // current tab
516 for (Tab t : mTabQueue) {
517 if (t != null && t.getWebView() != null) {
518 if (t != current && t != current.getParent()) {
519 return t;
520 }
521 }
522 }
523 return null;
524 }
525
The Android Open Source Project0c908882009-03-03 19:32:16 -0800526 /**
527 * Show the tab that contains the given WebView.
528 * @param view The WebView used to find the tab.
529 */
530 Tab getTabFromView(WebView view) {
Michael Kolbc831b632011-05-11 09:30:34 -0700531 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700532 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800533 return t;
534 }
535 }
536 return null;
537 }
538
539 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700540 * Return the tab with the matching application id.
541 * @param id The application identifier.
542 */
Michael Kolbc831b632011-05-11 09:30:34 -0700543 Tab getTabFromAppId(String id) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700544 if (id == null) {
545 return null;
546 }
Michael Kolbc831b632011-05-11 09:30:34 -0700547 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700548 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700549 return t;
550 }
551 }
552 return null;
553 }
554
Grace Kloba22ac16e2009-10-07 18:00:23 -0700555 /**
556 * Stop loading in all opened WebView including subWindows.
557 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700558 void stopAllLoading() {
Michael Kolbc831b632011-05-11 09:30:34 -0700559 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700560 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700561 if (webview != null) {
562 webview.stopLoading();
563 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700564 final WebView subview = t.getSubWebView();
565 if (subview != null) {
566 webview.stopLoading();
567 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700568 }
569 }
570
John Reckdb22ec42011-06-29 11:31:24 -0700571 // This method checks if a tab matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400572 private boolean tabMatchesUrl(Tab t, String url) {
John Reckdb22ec42011-06-29 11:31:24 -0700573 return url.equals(t.getUrl()) || url.equals(t.getOriginalUrl());
Patrick Scottcd115892009-07-16 09:42:58 -0400574 }
575
576 /**
John Reckdb22ec42011-06-29 11:31:24 -0700577 * Return the tab that matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400578 * @param url The url to search for.
579 */
John Reckdb22ec42011-06-29 11:31:24 -0700580 Tab findTabWithUrl(String url) {
Patrick Scottcd115892009-07-16 09:42:58 -0400581 if (url == null) {
582 return null;
583 }
584 // Check the current tab first.
John Reckdb22ec42011-06-29 11:31:24 -0700585 Tab currentTab = getCurrentTab();
586 if (currentTab != null && tabMatchesUrl(currentTab, url)) {
587 return currentTab;
Patrick Scottcd115892009-07-16 09:42:58 -0400588 }
589 // Now check all the rest.
Michael Kolbc831b632011-05-11 09:30:34 -0700590 for (Tab tab : mTabs) {
591 if (tabMatchesUrl(tab, url)) {
John Reckdb22ec42011-06-29 11:31:24 -0700592 return tab;
Patrick Scottcd115892009-07-16 09:42:58 -0400593 }
594 }
595 return null;
596 }
597
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700598 /**
John Reck30c714c2010-12-16 17:30:34 -0800599 * Recreate the main WebView of the given tab.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700600 */
John Reck30c714c2010-12-16 17:30:34 -0800601 void recreateWebView(Tab t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700602 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700603 if (w != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700604 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700605 }
606 // Create a new WebView. If this tab is the current tab, we need to put
607 // back all the clients so force it to be the current tab.
Steve Block2bc69912009-07-30 14:45:13 +0100608 t.setWebView(createNewWebView());
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700609 if (getCurrentTab() == t) {
610 setCurrentTab(t, true);
611 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700612 }
613
614 /**
615 * Creates a new WebView and registers it with the global settings.
616 */
617 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700618 return createNewWebView(false);
619 }
620
621 /**
622 * Creates a new WebView and registers it with the global settings.
623 * @param privateBrowsing When true, enables private browsing in the new
624 * WebView.
625 */
626 private WebView createNewWebView(boolean privateBrowsing) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700627 return mController.getWebViewFactory().createWebView(privateBrowsing);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700628 }
629
630 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800631 * Put the current tab in the background and set newTab as the current tab.
632 * @param newTab The new tab. If newTab is null, the current tab is not
633 * set.
634 */
635 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700636 return setCurrentTab(newTab, false);
637 }
638
639 /**
640 * If force is true, this method skips the check for newTab == current.
641 */
642 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800643 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700644 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800645 return true;
646 }
647 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700648 current.putInBackground();
649 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800650 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800651 if (newTab == null) {
652 return false;
653 }
654
655 // Move the newTab to the end of the queue
656 int index = mTabQueue.indexOf(newTab);
657 if (index != -1) {
658 mTabQueue.remove(index);
659 }
660 mTabQueue.add(newTab);
661
The Android Open Source Project0c908882009-03-03 19:32:16 -0800662 // Display the new current tab
663 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700664 WebView mainView = newTab.getWebView();
John Reckd8c74522011-06-14 08:45:00 -0700665 boolean needRestore = !newTab.isSnapshot() && (mainView == null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800666 if (needRestore) {
667 // Same work as in createNewTab() except don't do new Tab()
Steve Block2bc69912009-07-30 14:45:13 +0100668 mainView = createNewWebView();
669 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800670 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700671 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800672 return true;
673 }
Michael Kolbfe251992010-07-08 15:41:55 -0700674
John Reck8ee633f2011-08-09 16:00:35 -0700675 public void setOnThumbnailUpdatedListener(OnThumbnailUpdatedListener listener) {
676 mOnThumbnailUpdatedListener = listener;
677 for (Tab t : mTabs) {
678 WebView web = t.getWebView();
679 if (web != null) {
680 web.setPictureListener(listener != null ? t : null);
681 }
682 }
683 }
684
685 public OnThumbnailUpdatedListener getOnThumbnailUpdatedListener() {
686 return mOnThumbnailUpdatedListener;
687 }
688
The Android Open Source Project0c908882009-03-03 19:32:16 -0800689}