blob: 38a46a8c23b2a9fc6e67fd1354a078c9e5e90311 [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
23import java.io.File;
24import java.util.ArrayList;
Elliott Slaughter3d6df162010-08-25 13:17:44 -070025import java.util.HashMap;
Michael Kolb1bf23132010-11-19 12:55:12 -080026import java.util.List;
The Android Open Source Project0c908882009-03-03 19:32:16 -080027import java.util.Vector;
28
29class TabControl {
30 // Log Tag
31 private static final String LOGTAG = "TabControl";
Michael Kolbc831b632011-05-11 09:30:34 -070032
John Reckd8c74522011-06-14 08:45:00 -070033 // next Tab ID, starting at 1
34 private static long sNextId = 1;
Michael Kolbc831b632011-05-11 09:30:34 -070035
36 private static final String POSITIONS = "positions";
37 private static final String CURRENT = "current";
38
John Reck8ee633f2011-08-09 16:00:35 -070039 public static interface OnThumbnailUpdatedListener {
40 void onThumbnailUpdated(Tab t);
41 }
42
The Android Open Source Project0c908882009-03-03 19:32:16 -080043 // Maximum number of tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070044 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080045 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070046 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080047 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070048 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080049 // Current position in mTabs.
50 private int mCurrentTab = -1;
Michael Kolb8233fac2010-10-26 16:08:53 -070051 // the main browser controller
52 private final Controller mController;
53
The Android Open Source Project0c908882009-03-03 19:32:16 -080054 private final File mThumbnailDir;
John Reck8ee633f2011-08-09 16:00:35 -070055 private OnThumbnailUpdatedListener mOnThumbnailUpdatedListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080056
57 /**
Michael Kolb8233fac2010-10-26 16:08:53 -070058 * Construct a new TabControl object
The Android Open Source Project0c908882009-03-03 19:32:16 -080059 */
Michael Kolb8233fac2010-10-26 16:08:53 -070060 TabControl(Controller controller) {
61 mController = controller;
62 mThumbnailDir = mController.getActivity()
63 .getDir("thumbnails", 0);
64 mMaxTabs = mController.getMaxTabs();
Michael Kolb6e4653e2010-09-27 16:22:38 -070065 mTabs = new ArrayList<Tab>(mMaxTabs);
66 mTabQueue = new ArrayList<Tab>(mMaxTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -080067 }
68
Michael Kolbc831b632011-05-11 09:30:34 -070069 static long getNextId() {
70 return sNextId++;
71 }
72
The Android Open Source Project0c908882009-03-03 19:32:16 -080073 File getThumbnailDir() {
74 return mThumbnailDir;
75 }
76
Michael Kolb68775752010-08-19 12:42:01 -070077 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080078 * Return the current tab's main WebView. This will always return the main
79 * WebView for a given tab and not a subwindow.
80 * @return The current tab's WebView.
81 */
82 WebView getCurrentWebView() {
83 Tab t = getTab(mCurrentTab);
84 if (t == null) {
85 return null;
86 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070087 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +010088 }
89
90 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080091 * Return the current tab's top-level WebView. This can return a subwindow
92 * if one exists.
93 * @return The top-level WebView of the current tab.
94 */
95 WebView getCurrentTopWebView() {
96 Tab t = getTab(mCurrentTab);
97 if (t == null) {
98 return null;
99 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700100 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800101 }
102
103 /**
104 * Return the current tab's subwindow if it exists.
105 * @return The subwindow of the current tab or null if it doesn't exist.
106 */
107 WebView getCurrentSubWindow() {
108 Tab t = getTab(mCurrentTab);
109 if (t == null) {
110 return null;
111 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700112 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800113 }
114
115 /**
Michael Kolb1bf23132010-11-19 12:55:12 -0800116 * return the list of tabs
117 */
118 List<Tab> getTabs() {
119 return mTabs;
120 }
121
122 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700123 * Return the tab at the specified position.
124 * @return The Tab for the specified position or null if the tab does not
The Android Open Source Project0c908882009-03-03 19:32:16 -0800125 * exist.
126 */
Michael Kolbc831b632011-05-11 09:30:34 -0700127 Tab getTab(int position) {
128 if (position >= 0 && position < mTabs.size()) {
129 return mTabs.get(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800130 }
131 return null;
132 }
133
134 /**
135 * Return the current tab.
136 * @return The current tab.
137 */
138 Tab getCurrentTab() {
139 return getTab(mCurrentTab);
140 }
141
142 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700143 * Return the current tab position.
144 * @return The current tab position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800145 */
Michael Kolbc831b632011-05-11 09:30:34 -0700146 int getCurrentPosition() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800147 return mCurrentTab;
148 }
Michael Kolbfe251992010-07-08 15:41:55 -0700149
The Android Open Source Project0c908882009-03-03 19:32:16 -0800150 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700151 * Given a Tab, find it's position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800152 * @param Tab to find
Michael Kolbc831b632011-05-11 09:30:34 -0700153 * @return position of Tab or -1 if not found
The Android Open Source Project0c908882009-03-03 19:32:16 -0800154 */
Michael Kolbc831b632011-05-11 09:30:34 -0700155 int getTabPosition(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400156 if (tab == null) {
157 return -1;
158 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800159 return mTabs.indexOf(tab);
160 }
161
Grace Kloba22ac16e2009-10-07 18:00:23 -0700162 boolean canCreateNewTab() {
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100163 return mMaxTabs > mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700164 }
165
The Android Open Source Project0c908882009-03-03 19:32:16 -0800166 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700167 * Returns true if there are any incognito tabs open.
168 * @return True when any incognito tabs are open, false otherwise.
169 */
170 boolean hasAnyOpenIncognitoTabs() {
171 for (Tab tab : mTabs) {
Michael Kolbc831b632011-05-11 09:30:34 -0700172 if (tab.getWebView() != null
173 && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700174 return true;
175 }
176 }
177 return false;
178 }
179
Michael Kolb14612442011-06-24 13:06:29 -0700180 void addPreloadedTab(Tab tab) {
181 tab.setId(getNextId());
182 mTabs.add(tab);
183 tab.setController(mController);
184 mController.onSetWebView(tab, tab.getWebView());
185 tab.putInBackground();
186 }
187
Elliott Slaughtere440a882010-08-20 13:54:45 -0700188 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700189 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800190 * @return The newly createTab or null if we have reached the maximum
191 * number of open tabs.
192 */
Michael Kolb7bcafde2011-05-09 13:55:59 -0700193 Tab createNewTab(boolean privateBrowsing) {
John Reck1cf4b792011-07-26 10:22:22 -0700194 return createNewTab(null, privateBrowsing);
195 }
196
197 Tab createNewTab(Bundle state, boolean privateBrowsing) {
198 int size = mTabs.size();
199 // Return false if we have maxed out on tabs
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100200 if (!canCreateNewTab()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800201 return null;
202 }
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100203
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700204 final WebView w = createNewWebView(privateBrowsing);
Steve Block2bc69912009-07-30 14:45:13 +0100205
The Android Open Source Project0c908882009-03-03 19:32:16 -0800206 // Create a new tab and add it to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700207 Tab t = new Tab(mController, w, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700208 t.setId(getNextId());
The Android Open Source Project0c908882009-03-03 19:32:16 -0800209 mTabs.add(t);
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700210 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700211 t.putInBackground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800212 return t;
213 }
214
215 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700216 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700217 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700218 */
219 Tab createNewTab() {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700220 return createNewTab(false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700221 }
222
John Reckd8c74522011-06-14 08:45:00 -0700223 SnapshotTab createSnapshotTab(long snapshotId) {
John Reckd8c74522011-06-14 08:45:00 -0700224 SnapshotTab t = new SnapshotTab(mController, snapshotId);
225 t.setId(getNextId());
226 mTabs.add(t);
227 return t;
228 }
229
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700230 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500231 * Remove the parent child relationships from all tabs.
232 */
233 void removeParentChildRelationShips() {
234 for (Tab tab : mTabs) {
235 tab.removeFromTree();
236 }
237 }
238
239 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800240 * Remove the tab from the list. If the tab is the current tab shown, the
241 * last created tab will be shown.
242 * @param t The tab to be removed.
243 */
244 boolean removeTab(Tab t) {
245 if (t == null) {
246 return false;
247 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700248
Patrick Scottd944d4d2010-01-27 16:39:11 -0500249 // Grab the current tab before modifying the list.
250 Tab current = getCurrentTab();
251
252 // Remove t from our list of tabs.
253 mTabs.remove(t);
254
255 // Put the tab in the background only if it is the current one.
256 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700257 t.putInBackground();
258 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500259 } else {
260 // If a tab that is earlier in the list gets removed, the current
261 // index no longer points to the correct tab.
Michael Kolbc831b632011-05-11 09:30:34 -0700262 mCurrentTab = getTabPosition(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800263 }
264
Grace Kloba22ac16e2009-10-07 18:00:23 -0700265 // destroy the tab
266 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800267 // clear it's references to parent and children
268 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800269
The Android Open Source Project0c908882009-03-03 19:32:16 -0800270 // Remove it from the queue of viewed tabs.
271 mTabQueue.remove(t);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800272 return true;
273 }
274
275 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800276 * Destroy all the tabs and subwindows
277 */
278 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800279 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700280 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800281 }
282 mTabs.clear();
283 mTabQueue.clear();
284 }
285
286 /**
287 * Returns the number of tabs created.
288 * @return The number of tabs created.
289 */
290 int getTabCount() {
291 return mTabs.size();
292 }
293
The Android Open Source Project0c908882009-03-03 19:32:16 -0800294 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700295 * save the tab state:
296 * current position
297 * position sorted array of tab ids
298 * for each tab id, save the tab state
299 * @param outState
John Reckaed9c542011-05-27 16:08:53 -0700300 * @param saveImages
The Android Open Source Project0c908882009-03-03 19:32:16 -0800301 */
John Reck1cf4b792011-07-26 10:22:22 -0700302 void saveState(Bundle outState) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800303 final int numTabs = getTabCount();
John Reck24f18262011-06-17 14:47:20 -0700304 if (numTabs == 0) {
305 return;
306 }
Michael Kolbc831b632011-05-11 09:30:34 -0700307 long[] ids = new long[numTabs];
308 int i = 0;
309 for (Tab tab : mTabs) {
John Reck1cf4b792011-07-26 10:22:22 -0700310 Bundle tabState = tab.saveState();
311 if (tabState != null) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700312 ids[i++] = tab.getId();
John Reck1cf4b792011-07-26 10:22:22 -0700313 outState.putBundle(Long.toString(tab.getId()), tabState);
Michael Kolb3ae7f742011-07-13 15:18:25 -0700314 } else {
315 ids[i++] = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800316 }
317 }
John Reck24f18262011-06-17 14:47:20 -0700318 if (!outState.isEmpty()) {
319 outState.putLongArray(POSITIONS, ids);
320 Tab current = getCurrentTab();
321 long cid = -1;
322 if (current != null) {
323 cid = current.getId();
324 }
325 outState.putLong(CURRENT, cid);
Michael Kolbdbf39812011-06-10 14:06:40 -0700326 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800327 }
328
329 /**
Patrick Scott7d50a932011-02-04 09:27:26 -0500330 * Check if the state can be restored. If the state can be restored, the
Michael Kolbc831b632011-05-11 09:30:34 -0700331 * current tab id is returned. This can be passed to restoreState below
Patrick Scott7d50a932011-02-04 09:27:26 -0500332 * in order to restore the correct tab. Otherwise, -1 is returned and the
333 * state cannot be restored.
334 */
Michael Kolbc831b632011-05-11 09:30:34 -0700335 long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
336 final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
337 if (ids == null) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500338 return -1;
339 }
Michael Kolbc831b632011-05-11 09:30:34 -0700340 final long oldcurrent = inState.getLong(CURRENT);
341 long current = -1;
Michael Kolb3ae7f742011-07-13 15:18:25 -0700342 if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
John Reck1cf4b792011-07-26 10:22:22 -0700343 current = oldcurrent;
Patrick Scott7d50a932011-02-04 09:27:26 -0500344 } else {
Michael Kolbc831b632011-05-11 09:30:34 -0700345 // pick first non incognito tab
346 for (long id : ids) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700347 if (hasState(id, inState) && !isIncognito(id, inState)) {
Michael Kolbc831b632011-05-11 09:30:34 -0700348 current = id;
Patrick Scott7d50a932011-02-04 09:27:26 -0500349 break;
350 }
351 }
352 }
Michael Kolbc831b632011-05-11 09:30:34 -0700353 return current;
Patrick Scott7d50a932011-02-04 09:27:26 -0500354 }
355
Michael Kolb3ae7f742011-07-13 15:18:25 -0700356 private boolean hasState(long id, Bundle state) {
357 if (id == -1) return false;
358 Bundle tab = state.getBundle(Long.toString(id));
359 return ((tab != null) && !tab.isEmpty());
360 }
361
362 private boolean isIncognito(long id, Bundle state) {
363 Bundle tabstate = state.getBundle(Long.toString(id));
364 if ((tabstate != null) && !tabstate.isEmpty()) {
365 return tabstate.getBoolean(Tab.INCOGNITO);
366 }
367 return false;
368 }
369
Patrick Scott7d50a932011-02-04 09:27:26 -0500370 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800371 * Restore the state of all the tabs.
Michael Kolbc831b632011-05-11 09:30:34 -0700372 * @param currentId The tab id to restore.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800373 * @param inState The saved state of all the tabs.
Michael Kolb1bf23132010-11-19 12:55:12 -0800374 * @param restoreIncognitoTabs Restoring private browsing tabs
375 * @param restoreAll All webviews get restored, not just the current tab
376 * (this does not override handling of incognito tabs)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800377 */
Michael Kolbc831b632011-05-11 09:30:34 -0700378 void restoreState(Bundle inState, long currentId,
Patrick Scott7d50a932011-02-04 09:27:26 -0500379 boolean restoreIncognitoTabs, boolean restoreAll) {
Michael Kolbc831b632011-05-11 09:30:34 -0700380 if (currentId == -1) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500381 return;
382 }
Michael Kolbc831b632011-05-11 09:30:34 -0700383 long[] ids = inState.getLongArray(POSITIONS);
384 long maxId = -Long.MAX_VALUE;
385 HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>();
386 for (long id : ids) {
387 if (id > maxId) {
388 maxId = id;
389 }
390 final String idkey = Long.toString(id);
391 Bundle state = inState.getBundle(idkey);
John Reckd8c74522011-06-14 08:45:00 -0700392 if (state == null || state.isEmpty()) {
393 // Skip tab
394 continue;
395 } else if (!restoreIncognitoTabs
Michael Kolbc831b632011-05-11 09:30:34 -0700396 && state.getBoolean(Tab.INCOGNITO)) {
397 // ignore tab
398 } else if (id == currentId || restoreAll) {
John Reck1cf4b792011-07-26 10:22:22 -0700399 Tab t = createNewTab(state, false);
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100400 if (t == null) {
401 // We could "break" at this point, but we want
402 // sNextId to be set correctly.
403 continue;
404 }
Michael Kolbc831b632011-05-11 09:30:34 -0700405 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500406 // Me must set the current tab before restoring the state
407 // so that all the client classes are set.
Michael Kolbc831b632011-05-11 09:30:34 -0700408 if (id == currentId) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500409 setCurrentTab(t);
410 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700411 } else {
Patrick Scott7d50a932011-02-04 09:27:26 -0500412 // Create a new tab and don't restore the state yet, add it
413 // to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700414 Tab t = new Tab(mController, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700415 t.setId(id);
416 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500417 mTabs.add(t);
418 // added the tab to the front as they are not current
419 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700420 }
Michael Kolbc831b632011-05-11 09:30:34 -0700421 }
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100422
423 // make sure that there is no id overlap between the restored
424 // and new tabs
425 sNextId = maxId + 1;
426
John Reckd8c74522011-06-14 08:45:00 -0700427 if (mCurrentTab == -1) {
428 if (getTabCount() > 0) {
429 setCurrentTab(getTab(0));
430 } else {
431 Tab t = createNewTab();
432 setCurrentTab(t);
433 t.getWebView().loadUrl(BrowserSettings.getInstance()
434 .getHomePage());
435 }
436 }
Michael Kolbc831b632011-05-11 09:30:34 -0700437 // restore parent/child relationships
438 for (long id : ids) {
439 final Tab tab = tabMap.get(id);
440 final Bundle b = inState.getBundle(Long.toString(id));
441 if ((b != null) && (tab != null)) {
442 final long parentId = b.getLong(Tab.PARENTTAB, -1);
443 if (parentId != -1) {
444 final Tab parent = tabMap.get(parentId);
Patrick Scott7d50a932011-02-04 09:27:26 -0500445 if (parent != null) {
Michael Kolbc831b632011-05-11 09:30:34 -0700446 parent.addChildTab(tab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800447 }
448 }
449 }
450 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800451 }
452
453 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700454 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800455 * WebView cache;
456 */
457 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700458 if (getTabCount() == 0) return;
459
Grace Klobaf56f68d2010-04-11 22:53:42 -0700460 // free the least frequently used background tabs
461 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
462 if (tabs.size() > 0) {
463 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
464 for (Tab t : tabs) {
465 // store the WebView's state.
466 t.saveState();
467 // destroy the tab
468 t.destroy();
469 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800470 return;
471 }
472
Derek Sollenberger4433d032009-06-10 15:37:21 -0400473 // free the WebView's unused memory (this includes the cache)
474 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800475 WebView view = getCurrentWebView();
476 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400477 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800478 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800479 }
480
Grace Klobaf56f68d2010-04-11 22:53:42 -0700481 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
482 Vector<Tab> tabsToGo = new Vector<Tab>();
483
Patrick Scott2a67de42009-08-31 09:48:37 -0400484 // Don't do anything if we only have 1 tab or if the current tab is
485 // null.
486 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700487 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800488 }
489
Grace Klobaf56f68d2010-04-11 22:53:42 -0700490 if (mTabQueue.size() == 0) {
491 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800492 }
493
Grace Klobaf56f68d2010-04-11 22:53:42 -0700494 // Rip through the queue starting at the beginning and tear down half of
495 // available tabs which are not the current tab or the parent of the
496 // current tab.
497 int openTabCount = 0;
498 for (Tab t : mTabQueue) {
499 if (t != null && t.getWebView() != null) {
500 openTabCount++;
Michael Kolbc831b632011-05-11 09:30:34 -0700501 if (t != current && t != current.getParent()) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700502 tabsToGo.add(t);
503 }
504 }
505 }
506
507 openTabCount /= 2;
508 if (tabsToGo.size() > openTabCount) {
509 tabsToGo.setSize(openTabCount);
510 }
511
512 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800513 }
514
Michael Kolbff6a7482011-07-26 16:37:15 -0700515 Tab getLeastUsedTab(Tab current) {
516 if (getTabCount() == 1 || current == null) {
517 return null;
518 }
519 if (mTabQueue.size() == 0) {
520 return null;
521 }
522 // find a tab which is not the current tab or the parent of the
523 // current tab
524 for (Tab t : mTabQueue) {
525 if (t != null && t.getWebView() != null) {
526 if (t != current && t != current.getParent()) {
527 return t;
528 }
529 }
530 }
531 return null;
532 }
533
The Android Open Source Project0c908882009-03-03 19:32:16 -0800534 /**
535 * Show the tab that contains the given WebView.
536 * @param view The WebView used to find the tab.
537 */
538 Tab getTabFromView(WebView view) {
Michael Kolbc831b632011-05-11 09:30:34 -0700539 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700540 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800541 return t;
542 }
543 }
544 return null;
545 }
546
547 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700548 * Return the tab with the matching application id.
549 * @param id The application identifier.
550 */
Michael Kolbc831b632011-05-11 09:30:34 -0700551 Tab getTabFromAppId(String id) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700552 if (id == null) {
553 return null;
554 }
Michael Kolbc831b632011-05-11 09:30:34 -0700555 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700556 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700557 return t;
558 }
559 }
560 return null;
561 }
562
Grace Kloba22ac16e2009-10-07 18:00:23 -0700563 /**
564 * Stop loading in all opened WebView including subWindows.
565 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700566 void stopAllLoading() {
Michael Kolbc831b632011-05-11 09:30:34 -0700567 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700568 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700569 if (webview != null) {
570 webview.stopLoading();
571 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700572 final WebView subview = t.getSubWebView();
573 if (subview != null) {
574 webview.stopLoading();
575 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700576 }
577 }
578
John Reckdb22ec42011-06-29 11:31:24 -0700579 // This method checks if a tab matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400580 private boolean tabMatchesUrl(Tab t, String url) {
John Reckdb22ec42011-06-29 11:31:24 -0700581 return url.equals(t.getUrl()) || url.equals(t.getOriginalUrl());
Patrick Scottcd115892009-07-16 09:42:58 -0400582 }
583
584 /**
John Reckdb22ec42011-06-29 11:31:24 -0700585 * Return the tab that matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400586 * @param url The url to search for.
587 */
John Reckdb22ec42011-06-29 11:31:24 -0700588 Tab findTabWithUrl(String url) {
Patrick Scottcd115892009-07-16 09:42:58 -0400589 if (url == null) {
590 return null;
591 }
592 // Check the current tab first.
John Reckdb22ec42011-06-29 11:31:24 -0700593 Tab currentTab = getCurrentTab();
594 if (currentTab != null && tabMatchesUrl(currentTab, url)) {
595 return currentTab;
Patrick Scottcd115892009-07-16 09:42:58 -0400596 }
597 // Now check all the rest.
Michael Kolbc831b632011-05-11 09:30:34 -0700598 for (Tab tab : mTabs) {
599 if (tabMatchesUrl(tab, url)) {
John Reckdb22ec42011-06-29 11:31:24 -0700600 return tab;
Patrick Scottcd115892009-07-16 09:42:58 -0400601 }
602 }
603 return null;
604 }
605
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700606 /**
John Reck30c714c2010-12-16 17:30:34 -0800607 * Recreate the main WebView of the given tab.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700608 */
John Reck30c714c2010-12-16 17:30:34 -0800609 void recreateWebView(Tab t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700610 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700611 if (w != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700612 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700613 }
614 // Create a new WebView. If this tab is the current tab, we need to put
615 // back all the clients so force it to be the current tab.
Steve Block2bc69912009-07-30 14:45:13 +0100616 t.setWebView(createNewWebView());
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700617 if (getCurrentTab() == t) {
618 setCurrentTab(t, true);
619 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700620 }
621
622 /**
623 * Creates a new WebView and registers it with the global settings.
624 */
625 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700626 return createNewWebView(false);
627 }
628
629 /**
630 * Creates a new WebView and registers it with the global settings.
631 * @param privateBrowsing When true, enables private browsing in the new
632 * WebView.
633 */
634 private WebView createNewWebView(boolean privateBrowsing) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700635 return mController.getWebViewFactory().createWebView(privateBrowsing);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700636 }
637
638 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800639 * Put the current tab in the background and set newTab as the current tab.
640 * @param newTab The new tab. If newTab is null, the current tab is not
641 * set.
642 */
643 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700644 return setCurrentTab(newTab, false);
645 }
646
647 /**
648 * If force is true, this method skips the check for newTab == current.
649 */
650 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800651 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700652 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800653 return true;
654 }
655 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700656 current.putInBackground();
657 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800658 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800659 if (newTab == null) {
660 return false;
661 }
662
663 // Move the newTab to the end of the queue
664 int index = mTabQueue.indexOf(newTab);
665 if (index != -1) {
666 mTabQueue.remove(index);
667 }
668 mTabQueue.add(newTab);
669
The Android Open Source Project0c908882009-03-03 19:32:16 -0800670 // Display the new current tab
671 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700672 WebView mainView = newTab.getWebView();
John Reckd8c74522011-06-14 08:45:00 -0700673 boolean needRestore = !newTab.isSnapshot() && (mainView == null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800674 if (needRestore) {
675 // Same work as in createNewTab() except don't do new Tab()
Steve Block2bc69912009-07-30 14:45:13 +0100676 mainView = createNewWebView();
677 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800678 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700679 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800680 return true;
681 }
Michael Kolbfe251992010-07-08 15:41:55 -0700682
John Reck8ee633f2011-08-09 16:00:35 -0700683 public void setOnThumbnailUpdatedListener(OnThumbnailUpdatedListener listener) {
684 mOnThumbnailUpdatedListener = listener;
685 for (Tab t : mTabs) {
686 WebView web = t.getWebView();
687 if (web != null) {
688 web.setPictureListener(listener != null ? t : null);
689 }
690 }
691 }
692
693 public OnThumbnailUpdatedListener getOnThumbnailUpdatedListener() {
694 return mOnThumbnailUpdatedListener;
695 }
696
The Android Open Source Project0c908882009-03-03 19:32:16 -0800697}