blob: b70884133cccc6a9bd2d5b169ecb47bba0bfbec1 [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
The Android Open Source Project0c908882009-03-03 19:32:16 -080039 // Maximum number of tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070040 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080041 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070042 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080043 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070044 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080045 // Current position in mTabs.
46 private int mCurrentTab = -1;
Michael Kolb8233fac2010-10-26 16:08:53 -070047 // the main browser controller
48 private final Controller mController;
49
The Android Open Source Project0c908882009-03-03 19:32:16 -080050 private final File mThumbnailDir;
51
52 /**
Michael Kolb8233fac2010-10-26 16:08:53 -070053 * Construct a new TabControl object
The Android Open Source Project0c908882009-03-03 19:32:16 -080054 */
Michael Kolb8233fac2010-10-26 16:08:53 -070055 TabControl(Controller controller) {
56 mController = controller;
57 mThumbnailDir = mController.getActivity()
58 .getDir("thumbnails", 0);
59 mMaxTabs = mController.getMaxTabs();
Michael Kolb6e4653e2010-09-27 16:22:38 -070060 mTabs = new ArrayList<Tab>(mMaxTabs);
61 mTabQueue = new ArrayList<Tab>(mMaxTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -080062 }
63
Michael Kolbc831b632011-05-11 09:30:34 -070064 static long getNextId() {
65 return sNextId++;
66 }
67
The Android Open Source Project0c908882009-03-03 19:32:16 -080068 File getThumbnailDir() {
69 return mThumbnailDir;
70 }
71
Michael Kolb68775752010-08-19 12:42:01 -070072 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080073 * Return the current tab's main WebView. This will always return the main
74 * WebView for a given tab and not a subwindow.
75 * @return The current tab's WebView.
76 */
77 WebView getCurrentWebView() {
78 Tab t = getTab(mCurrentTab);
79 if (t == null) {
80 return null;
81 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070082 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +010083 }
84
85 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080086 * Return the current tab's top-level WebView. This can return a subwindow
87 * if one exists.
88 * @return The top-level WebView of the current tab.
89 */
90 WebView getCurrentTopWebView() {
91 Tab t = getTab(mCurrentTab);
92 if (t == null) {
93 return null;
94 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070095 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -080096 }
97
98 /**
99 * Return the current tab's subwindow if it exists.
100 * @return The subwindow of the current tab or null if it doesn't exist.
101 */
102 WebView getCurrentSubWindow() {
103 Tab t = getTab(mCurrentTab);
104 if (t == null) {
105 return null;
106 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700107 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800108 }
109
110 /**
Michael Kolb1bf23132010-11-19 12:55:12 -0800111 * return the list of tabs
112 */
113 List<Tab> getTabs() {
114 return mTabs;
115 }
116
117 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700118 * Return the tab at the specified position.
119 * @return The Tab for the specified position or null if the tab does not
The Android Open Source Project0c908882009-03-03 19:32:16 -0800120 * exist.
121 */
Michael Kolbc831b632011-05-11 09:30:34 -0700122 Tab getTab(int position) {
123 if (position >= 0 && position < mTabs.size()) {
124 return mTabs.get(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800125 }
126 return null;
127 }
128
129 /**
130 * Return the current tab.
131 * @return The current tab.
132 */
133 Tab getCurrentTab() {
134 return getTab(mCurrentTab);
135 }
136
137 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700138 * Return the current tab position.
139 * @return The current tab position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800140 */
Michael Kolbc831b632011-05-11 09:30:34 -0700141 int getCurrentPosition() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800142 return mCurrentTab;
143 }
Michael Kolbfe251992010-07-08 15:41:55 -0700144
The Android Open Source Project0c908882009-03-03 19:32:16 -0800145 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700146 * Given a Tab, find it's position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800147 * @param Tab to find
Michael Kolbc831b632011-05-11 09:30:34 -0700148 * @return position of Tab or -1 if not found
The Android Open Source Project0c908882009-03-03 19:32:16 -0800149 */
Michael Kolbc831b632011-05-11 09:30:34 -0700150 int getTabPosition(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400151 if (tab == null) {
152 return -1;
153 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800154 return mTabs.indexOf(tab);
155 }
156
Grace Kloba22ac16e2009-10-07 18:00:23 -0700157 boolean canCreateNewTab() {
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100158 return mMaxTabs > mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700159 }
160
The Android Open Source Project0c908882009-03-03 19:32:16 -0800161 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700162 * Returns true if there are any incognito tabs open.
163 * @return True when any incognito tabs are open, false otherwise.
164 */
165 boolean hasAnyOpenIncognitoTabs() {
166 for (Tab tab : mTabs) {
Michael Kolbc831b632011-05-11 09:30:34 -0700167 if (tab.getWebView() != null
168 && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700169 return true;
170 }
171 }
172 return false;
173 }
174
Michael Kolb14612442011-06-24 13:06:29 -0700175 void addPreloadedTab(Tab tab) {
176 tab.setId(getNextId());
177 mTabs.add(tab);
178 tab.setController(mController);
179 mController.onSetWebView(tab, tab.getWebView());
180 tab.putInBackground();
181 }
182
Elliott Slaughtere440a882010-08-20 13:54:45 -0700183 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700184 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800185 * @return The newly createTab or null if we have reached the maximum
186 * number of open tabs.
187 */
Michael Kolb7bcafde2011-05-09 13:55:59 -0700188 Tab createNewTab(boolean privateBrowsing) {
John Reck1cf4b792011-07-26 10:22:22 -0700189 return createNewTab(null, privateBrowsing);
190 }
191
192 Tab createNewTab(Bundle state, boolean privateBrowsing) {
193 int size = mTabs.size();
194 // Return false if we have maxed out on tabs
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100195 if (!canCreateNewTab()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800196 return null;
197 }
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100198
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700199 final WebView w = createNewWebView(privateBrowsing);
Steve Block2bc69912009-07-30 14:45:13 +0100200
The Android Open Source Project0c908882009-03-03 19:32:16 -0800201 // Create a new tab and add it to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700202 Tab t = new Tab(mController, w, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700203 t.setId(getNextId());
The Android Open Source Project0c908882009-03-03 19:32:16 -0800204 mTabs.add(t);
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700205 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700206 t.putInBackground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800207 return t;
208 }
209
210 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700211 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700212 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700213 */
214 Tab createNewTab() {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700215 return createNewTab(false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700216 }
217
John Reckd8c74522011-06-14 08:45:00 -0700218 SnapshotTab createSnapshotTab(long snapshotId) {
John Reckd8c74522011-06-14 08:45:00 -0700219 SnapshotTab t = new SnapshotTab(mController, snapshotId);
220 t.setId(getNextId());
221 mTabs.add(t);
222 return t;
223 }
224
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700225 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500226 * Remove the parent child relationships from all tabs.
227 */
228 void removeParentChildRelationShips() {
229 for (Tab tab : mTabs) {
230 tab.removeFromTree();
231 }
232 }
233
234 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800235 * Remove the tab from the list. If the tab is the current tab shown, the
236 * last created tab will be shown.
237 * @param t The tab to be removed.
238 */
239 boolean removeTab(Tab t) {
240 if (t == null) {
241 return false;
242 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700243
Patrick Scottd944d4d2010-01-27 16:39:11 -0500244 // Grab the current tab before modifying the list.
245 Tab current = getCurrentTab();
246
247 // Remove t from our list of tabs.
248 mTabs.remove(t);
249
250 // Put the tab in the background only if it is the current one.
251 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700252 t.putInBackground();
253 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500254 } else {
255 // If a tab that is earlier in the list gets removed, the current
256 // index no longer points to the correct tab.
Michael Kolbc831b632011-05-11 09:30:34 -0700257 mCurrentTab = getTabPosition(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800258 }
259
Grace Kloba22ac16e2009-10-07 18:00:23 -0700260 // destroy the tab
261 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800262 // clear it's references to parent and children
263 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800264
The Android Open Source Project0c908882009-03-03 19:32:16 -0800265 // Remove it from the queue of viewed tabs.
266 mTabQueue.remove(t);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800267 return true;
268 }
269
270 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800271 * Destroy all the tabs and subwindows
272 */
273 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800274 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700275 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800276 }
277 mTabs.clear();
278 mTabQueue.clear();
279 }
280
281 /**
282 * Returns the number of tabs created.
283 * @return The number of tabs created.
284 */
285 int getTabCount() {
286 return mTabs.size();
287 }
288
The Android Open Source Project0c908882009-03-03 19:32:16 -0800289 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700290 * save the tab state:
291 * current position
292 * position sorted array of tab ids
293 * for each tab id, save the tab state
294 * @param outState
John Reckaed9c542011-05-27 16:08:53 -0700295 * @param saveImages
The Android Open Source Project0c908882009-03-03 19:32:16 -0800296 */
John Reck1cf4b792011-07-26 10:22:22 -0700297 void saveState(Bundle outState) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800298 final int numTabs = getTabCount();
John Reck24f18262011-06-17 14:47:20 -0700299 if (numTabs == 0) {
300 return;
301 }
Michael Kolbc831b632011-05-11 09:30:34 -0700302 long[] ids = new long[numTabs];
303 int i = 0;
304 for (Tab tab : mTabs) {
John Reck1cf4b792011-07-26 10:22:22 -0700305 Bundle tabState = tab.saveState();
306 if (tabState != null) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700307 ids[i++] = tab.getId();
John Reck1cf4b792011-07-26 10:22:22 -0700308 outState.putBundle(Long.toString(tab.getId()), tabState);
Michael Kolb3ae7f742011-07-13 15:18:25 -0700309 } else {
310 ids[i++] = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800311 }
312 }
John Reck24f18262011-06-17 14:47:20 -0700313 if (!outState.isEmpty()) {
314 outState.putLongArray(POSITIONS, ids);
315 Tab current = getCurrentTab();
316 long cid = -1;
317 if (current != null) {
318 cid = current.getId();
319 }
320 outState.putLong(CURRENT, cid);
Michael Kolbdbf39812011-06-10 14:06:40 -0700321 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800322 }
323
324 /**
Patrick Scott7d50a932011-02-04 09:27:26 -0500325 * Check if the state can be restored. If the state can be restored, the
Michael Kolbc831b632011-05-11 09:30:34 -0700326 * current tab id is returned. This can be passed to restoreState below
Patrick Scott7d50a932011-02-04 09:27:26 -0500327 * in order to restore the correct tab. Otherwise, -1 is returned and the
328 * state cannot be restored.
329 */
Michael Kolbc831b632011-05-11 09:30:34 -0700330 long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
331 final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
332 if (ids == null) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500333 return -1;
334 }
Michael Kolbc831b632011-05-11 09:30:34 -0700335 final long oldcurrent = inState.getLong(CURRENT);
336 long current = -1;
Michael Kolb3ae7f742011-07-13 15:18:25 -0700337 if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
John Reck1cf4b792011-07-26 10:22:22 -0700338 current = oldcurrent;
Patrick Scott7d50a932011-02-04 09:27:26 -0500339 } else {
Michael Kolbc831b632011-05-11 09:30:34 -0700340 // pick first non incognito tab
341 for (long id : ids) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700342 if (hasState(id, inState) && !isIncognito(id, inState)) {
Michael Kolbc831b632011-05-11 09:30:34 -0700343 current = id;
Patrick Scott7d50a932011-02-04 09:27:26 -0500344 break;
345 }
346 }
347 }
Michael Kolbc831b632011-05-11 09:30:34 -0700348 return current;
Patrick Scott7d50a932011-02-04 09:27:26 -0500349 }
350
Michael Kolb3ae7f742011-07-13 15:18:25 -0700351 private boolean hasState(long id, Bundle state) {
352 if (id == -1) return false;
353 Bundle tab = state.getBundle(Long.toString(id));
354 return ((tab != null) && !tab.isEmpty());
355 }
356
357 private boolean isIncognito(long id, Bundle state) {
358 Bundle tabstate = state.getBundle(Long.toString(id));
359 if ((tabstate != null) && !tabstate.isEmpty()) {
360 return tabstate.getBoolean(Tab.INCOGNITO);
361 }
362 return false;
363 }
364
Patrick Scott7d50a932011-02-04 09:27:26 -0500365 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800366 * Restore the state of all the tabs.
Michael Kolbc831b632011-05-11 09:30:34 -0700367 * @param currentId The tab id to restore.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800368 * @param inState The saved state of all the tabs.
Michael Kolb1bf23132010-11-19 12:55:12 -0800369 * @param restoreIncognitoTabs Restoring private browsing tabs
370 * @param restoreAll All webviews get restored, not just the current tab
371 * (this does not override handling of incognito tabs)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800372 */
Michael Kolbc831b632011-05-11 09:30:34 -0700373 void restoreState(Bundle inState, long currentId,
Patrick Scott7d50a932011-02-04 09:27:26 -0500374 boolean restoreIncognitoTabs, boolean restoreAll) {
Michael Kolbc831b632011-05-11 09:30:34 -0700375 if (currentId == -1) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500376 return;
377 }
Michael Kolbc831b632011-05-11 09:30:34 -0700378 long[] ids = inState.getLongArray(POSITIONS);
379 long maxId = -Long.MAX_VALUE;
380 HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>();
381 for (long id : ids) {
382 if (id > maxId) {
383 maxId = id;
384 }
385 final String idkey = Long.toString(id);
386 Bundle state = inState.getBundle(idkey);
John Reckd8c74522011-06-14 08:45:00 -0700387 if (state == null || state.isEmpty()) {
388 // Skip tab
389 continue;
390 } else if (!restoreIncognitoTabs
Michael Kolbc831b632011-05-11 09:30:34 -0700391 && state.getBoolean(Tab.INCOGNITO)) {
392 // ignore tab
393 } else if (id == currentId || restoreAll) {
John Reck1cf4b792011-07-26 10:22:22 -0700394 Tab t = createNewTab(state, false);
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100395 if (t == null) {
396 // We could "break" at this point, but we want
397 // sNextId to be set correctly.
398 continue;
399 }
Michael Kolbc831b632011-05-11 09:30:34 -0700400 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500401 // Me must set the current tab before restoring the state
402 // so that all the client classes are set.
Michael Kolbc831b632011-05-11 09:30:34 -0700403 if (id == currentId) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500404 setCurrentTab(t);
405 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700406 } else {
Patrick Scott7d50a932011-02-04 09:27:26 -0500407 // Create a new tab and don't restore the state yet, add it
408 // to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700409 Tab t = new Tab(mController, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700410 t.setId(id);
411 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500412 mTabs.add(t);
413 // added the tab to the front as they are not current
414 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700415 }
Michael Kolbc831b632011-05-11 09:30:34 -0700416 }
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100417
418 // make sure that there is no id overlap between the restored
419 // and new tabs
420 sNextId = maxId + 1;
421
John Reckd8c74522011-06-14 08:45:00 -0700422 if (mCurrentTab == -1) {
423 if (getTabCount() > 0) {
424 setCurrentTab(getTab(0));
425 } else {
426 Tab t = createNewTab();
427 setCurrentTab(t);
428 t.getWebView().loadUrl(BrowserSettings.getInstance()
429 .getHomePage());
430 }
431 }
Michael Kolbc831b632011-05-11 09:30:34 -0700432 // restore parent/child relationships
433 for (long id : ids) {
434 final Tab tab = tabMap.get(id);
435 final Bundle b = inState.getBundle(Long.toString(id));
436 if ((b != null) && (tab != null)) {
437 final long parentId = b.getLong(Tab.PARENTTAB, -1);
438 if (parentId != -1) {
439 final Tab parent = tabMap.get(parentId);
Patrick Scott7d50a932011-02-04 09:27:26 -0500440 if (parent != null) {
Michael Kolbc831b632011-05-11 09:30:34 -0700441 parent.addChildTab(tab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800442 }
443 }
444 }
445 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800446 }
447
448 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700449 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800450 * WebView cache;
451 */
452 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700453 if (getTabCount() == 0) return;
454
Grace Klobaf56f68d2010-04-11 22:53:42 -0700455 // free the least frequently used background tabs
456 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
457 if (tabs.size() > 0) {
458 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
459 for (Tab t : tabs) {
460 // store the WebView's state.
461 t.saveState();
462 // destroy the tab
463 t.destroy();
464 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800465 return;
466 }
467
Derek Sollenberger4433d032009-06-10 15:37:21 -0400468 // free the WebView's unused memory (this includes the cache)
469 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800470 WebView view = getCurrentWebView();
471 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400472 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800473 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800474 }
475
Grace Klobaf56f68d2010-04-11 22:53:42 -0700476 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
477 Vector<Tab> tabsToGo = new Vector<Tab>();
478
Patrick Scott2a67de42009-08-31 09:48:37 -0400479 // Don't do anything if we only have 1 tab or if the current tab is
480 // null.
481 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700482 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800483 }
484
Grace Klobaf56f68d2010-04-11 22:53:42 -0700485 if (mTabQueue.size() == 0) {
486 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800487 }
488
Grace Klobaf56f68d2010-04-11 22:53:42 -0700489 // Rip through the queue starting at the beginning and tear down half of
490 // available tabs which are not the current tab or the parent of the
491 // current tab.
492 int openTabCount = 0;
493 for (Tab t : mTabQueue) {
494 if (t != null && t.getWebView() != null) {
495 openTabCount++;
Michael Kolbc831b632011-05-11 09:30:34 -0700496 if (t != current && t != current.getParent()) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700497 tabsToGo.add(t);
498 }
499 }
500 }
501
502 openTabCount /= 2;
503 if (tabsToGo.size() > openTabCount) {
504 tabsToGo.setSize(openTabCount);
505 }
506
507 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800508 }
509
Michael Kolbff6a7482011-07-26 16:37:15 -0700510 Tab getLeastUsedTab(Tab current) {
511 if (getTabCount() == 1 || current == null) {
512 return null;
513 }
514 if (mTabQueue.size() == 0) {
515 return null;
516 }
517 // find a tab which is not the current tab or the parent of the
518 // current tab
519 for (Tab t : mTabQueue) {
520 if (t != null && t.getWebView() != null) {
521 if (t != current && t != current.getParent()) {
522 return t;
523 }
524 }
525 }
526 return null;
527 }
528
The Android Open Source Project0c908882009-03-03 19:32:16 -0800529 /**
530 * Show the tab that contains the given WebView.
531 * @param view The WebView used to find the tab.
532 */
533 Tab getTabFromView(WebView view) {
Michael Kolbc831b632011-05-11 09:30:34 -0700534 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700535 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800536 return t;
537 }
538 }
539 return null;
540 }
541
542 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700543 * Return the tab with the matching application id.
544 * @param id The application identifier.
545 */
Michael Kolbc831b632011-05-11 09:30:34 -0700546 Tab getTabFromAppId(String id) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700547 if (id == null) {
548 return null;
549 }
Michael Kolbc831b632011-05-11 09:30:34 -0700550 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700551 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700552 return t;
553 }
554 }
555 return null;
556 }
557
Grace Kloba22ac16e2009-10-07 18:00:23 -0700558 /**
559 * Stop loading in all opened WebView including subWindows.
560 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700561 void stopAllLoading() {
Michael Kolbc831b632011-05-11 09:30:34 -0700562 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700563 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700564 if (webview != null) {
565 webview.stopLoading();
566 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700567 final WebView subview = t.getSubWebView();
568 if (subview != null) {
569 webview.stopLoading();
570 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700571 }
572 }
573
John Reckdb22ec42011-06-29 11:31:24 -0700574 // This method checks if a tab matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400575 private boolean tabMatchesUrl(Tab t, String url) {
John Reckdb22ec42011-06-29 11:31:24 -0700576 return url.equals(t.getUrl()) || url.equals(t.getOriginalUrl());
Patrick Scottcd115892009-07-16 09:42:58 -0400577 }
578
579 /**
John Reckdb22ec42011-06-29 11:31:24 -0700580 * Return the tab that matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400581 * @param url The url to search for.
582 */
John Reckdb22ec42011-06-29 11:31:24 -0700583 Tab findTabWithUrl(String url) {
Patrick Scottcd115892009-07-16 09:42:58 -0400584 if (url == null) {
585 return null;
586 }
587 // Check the current tab first.
John Reckdb22ec42011-06-29 11:31:24 -0700588 Tab currentTab = getCurrentTab();
589 if (currentTab != null && tabMatchesUrl(currentTab, url)) {
590 return currentTab;
Patrick Scottcd115892009-07-16 09:42:58 -0400591 }
592 // Now check all the rest.
Michael Kolbc831b632011-05-11 09:30:34 -0700593 for (Tab tab : mTabs) {
594 if (tabMatchesUrl(tab, url)) {
John Reckdb22ec42011-06-29 11:31:24 -0700595 return tab;
Patrick Scottcd115892009-07-16 09:42:58 -0400596 }
597 }
598 return null;
599 }
600
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700601 /**
John Reck30c714c2010-12-16 17:30:34 -0800602 * Recreate the main WebView of the given tab.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700603 */
John Reck30c714c2010-12-16 17:30:34 -0800604 void recreateWebView(Tab t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700605 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700606 if (w != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700607 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700608 }
609 // Create a new WebView. If this tab is the current tab, we need to put
610 // back all the clients so force it to be the current tab.
Steve Block2bc69912009-07-30 14:45:13 +0100611 t.setWebView(createNewWebView());
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700612 if (getCurrentTab() == t) {
613 setCurrentTab(t, true);
614 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700615 }
616
617 /**
618 * Creates a new WebView and registers it with the global settings.
619 */
620 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700621 return createNewWebView(false);
622 }
623
624 /**
625 * Creates a new WebView and registers it with the global settings.
626 * @param privateBrowsing When true, enables private browsing in the new
627 * WebView.
628 */
629 private WebView createNewWebView(boolean privateBrowsing) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700630 return mController.getWebViewFactory().createWebView(privateBrowsing);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700631 }
632
633 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800634 * Put the current tab in the background and set newTab as the current tab.
635 * @param newTab The new tab. If newTab is null, the current tab is not
636 * set.
637 */
638 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700639 return setCurrentTab(newTab, false);
640 }
641
642 /**
643 * If force is true, this method skips the check for newTab == current.
644 */
645 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800646 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700647 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800648 return true;
649 }
650 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700651 current.putInBackground();
652 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800653 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800654 if (newTab == null) {
655 return false;
656 }
657
658 // Move the newTab to the end of the queue
659 int index = mTabQueue.indexOf(newTab);
660 if (index != -1) {
661 mTabQueue.remove(index);
662 }
663 mTabQueue.add(newTab);
664
The Android Open Source Project0c908882009-03-03 19:32:16 -0800665 // Display the new current tab
666 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700667 WebView mainView = newTab.getWebView();
John Reckd8c74522011-06-14 08:45:00 -0700668 boolean needRestore = !newTab.isSnapshot() && (mainView == null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800669 if (needRestore) {
670 // Same work as in createNewTab() except don't do new Tab()
Steve Block2bc69912009-07-30 14:45:13 +0100671 mainView = createNewWebView();
672 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800673 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700674 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800675 return true;
676 }
Michael Kolbfe251992010-07-08 15:41:55 -0700677
The Android Open Source Project0c908882009-03-03 19:32:16 -0800678}