blob: 10de8a107d2e27810c5e20985a9879f2ddd75229 [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;
qqzhou8c5b0a32013-07-22 15:31:03 +080020import android.os.SystemProperties;
The Android Open Source Project0c908882009-03-03 19:32:16 -080021import android.util.Log;
The Android Open Source Project0c908882009-03-03 19:32:16 -080022import android.webkit.WebView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080023
The Android Open Source Project0c908882009-03-03 19:32:16 -080024import 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
John Reck8ee633f2011-08-09 16:00:35 -070054 private OnThumbnailUpdatedListener mOnThumbnailUpdatedListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080055
56 /**
Michael Kolb8233fac2010-10-26 16:08:53 -070057 * Construct a new TabControl object
The Android Open Source Project0c908882009-03-03 19:32:16 -080058 */
Michael Kolb8233fac2010-10-26 16:08:53 -070059 TabControl(Controller controller) {
60 mController = controller;
Michael Kolb8233fac2010-10-26 16:08:53 -070061 mMaxTabs = mController.getMaxTabs();
Michael Kolb6e4653e2010-09-27 16:22:38 -070062 mTabs = new ArrayList<Tab>(mMaxTabs);
63 mTabQueue = new ArrayList<Tab>(mMaxTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -080064 }
65
John Reck52be4782011-08-26 15:37:29 -070066 synchronized static long getNextId() {
Michael Kolbc831b632011-05-11 09:30:34 -070067 return sNextId++;
68 }
69
Michael Kolb68775752010-08-19 12:42:01 -070070 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080071 * Return the current tab's main WebView. This will always return the main
72 * WebView for a given tab and not a subwindow.
73 * @return The current tab's WebView.
74 */
75 WebView getCurrentWebView() {
76 Tab t = getTab(mCurrentTab);
77 if (t == null) {
78 return null;
79 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070080 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +010081 }
82
83 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080084 * Return the current tab's top-level WebView. This can return a subwindow
85 * if one exists.
86 * @return The top-level WebView of the current tab.
87 */
88 WebView getCurrentTopWebView() {
89 Tab t = getTab(mCurrentTab);
90 if (t == null) {
91 return null;
92 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070093 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -080094 }
95
96 /**
97 * Return the current tab's subwindow if it exists.
98 * @return The subwindow of the current tab or null if it doesn't exist.
99 */
100 WebView getCurrentSubWindow() {
101 Tab t = getTab(mCurrentTab);
102 if (t == null) {
103 return null;
104 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700105 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800106 }
107
108 /**
Michael Kolb1bf23132010-11-19 12:55:12 -0800109 * return the list of tabs
110 */
111 List<Tab> getTabs() {
112 return mTabs;
113 }
114
115 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700116 * Return the tab at the specified position.
117 * @return The Tab for the specified position or null if the tab does not
The Android Open Source Project0c908882009-03-03 19:32:16 -0800118 * exist.
119 */
Michael Kolbc831b632011-05-11 09:30:34 -0700120 Tab getTab(int position) {
121 if (position >= 0 && position < mTabs.size()) {
122 return mTabs.get(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800123 }
124 return null;
125 }
126
127 /**
128 * Return the current tab.
129 * @return The current tab.
130 */
131 Tab getCurrentTab() {
132 return getTab(mCurrentTab);
133 }
134
135 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700136 * Return the current tab position.
137 * @return The current tab position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800138 */
Michael Kolbc831b632011-05-11 09:30:34 -0700139 int getCurrentPosition() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800140 return mCurrentTab;
141 }
Michael Kolbfe251992010-07-08 15:41:55 -0700142
The Android Open Source Project0c908882009-03-03 19:32:16 -0800143 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700144 * Given a Tab, find it's position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800145 * @param Tab to find
Michael Kolbc831b632011-05-11 09:30:34 -0700146 * @return position of Tab or -1 if not found
The Android Open Source Project0c908882009-03-03 19:32:16 -0800147 */
Michael Kolbc831b632011-05-11 09:30:34 -0700148 int getTabPosition(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400149 if (tab == null) {
150 return -1;
151 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800152 return mTabs.indexOf(tab);
153 }
154
Grace Kloba22ac16e2009-10-07 18:00:23 -0700155 boolean canCreateNewTab() {
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100156 return mMaxTabs > mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700157 }
158
The Android Open Source Project0c908882009-03-03 19:32:16 -0800159 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700160 * Returns true if there are any incognito tabs open.
161 * @return True when any incognito tabs are open, false otherwise.
162 */
163 boolean hasAnyOpenIncognitoTabs() {
164 for (Tab tab : mTabs) {
Michael Kolbc831b632011-05-11 09:30:34 -0700165 if (tab.getWebView() != null
166 && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700167 return true;
168 }
169 }
170 return false;
171 }
172
Michael Kolb14612442011-06-24 13:06:29 -0700173 void addPreloadedTab(Tab tab) {
Mathew Inwoode09305e2011-09-02 12:03:26 +0100174 for (Tab current : mTabs) {
175 if (current != null && current.getId() == tab.getId()) {
176 throw new IllegalStateException("Tab with id " + tab.getId() + " already exists: "
177 + current.toString());
178 }
179 }
Michael Kolb14612442011-06-24 13:06:29 -0700180 mTabs.add(tab);
181 tab.setController(mController);
182 mController.onSetWebView(tab, tab.getWebView());
183 tab.putInBackground();
184 }
185
Elliott Slaughtere440a882010-08-20 13:54:45 -0700186 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700187 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800188 * @return The newly createTab or null if we have reached the maximum
189 * number of open tabs.
190 */
Michael Kolb7bcafde2011-05-09 13:55:59 -0700191 Tab createNewTab(boolean privateBrowsing) {
John Reck1cf4b792011-07-26 10:22:22 -0700192 return createNewTab(null, privateBrowsing);
193 }
194
195 Tab createNewTab(Bundle state, boolean privateBrowsing) {
196 int size = mTabs.size();
197 // Return false if we have maxed out on tabs
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100198 if (!canCreateNewTab()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800199 return null;
200 }
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100201
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700202 final WebView w = createNewWebView(privateBrowsing);
Steve Block2bc69912009-07-30 14:45:13 +0100203
The Android Open Source Project0c908882009-03-03 19:32:16 -0800204 // Create a new tab and add it to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700205 Tab t = new Tab(mController, w, state);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800206 mTabs.add(t);
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700207 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700208 t.putInBackground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800209 return t;
210 }
211
212 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700213 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700214 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700215 */
216 Tab createNewTab() {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700217 return createNewTab(false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700218 }
219
John Reckd8c74522011-06-14 08:45:00 -0700220 SnapshotTab createSnapshotTab(long snapshotId) {
John Reckd8c74522011-06-14 08:45:00 -0700221 SnapshotTab t = new SnapshotTab(mController, snapshotId);
John Reckd8c74522011-06-14 08:45:00 -0700222 mTabs.add(t);
223 return t;
224 }
225
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700226 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500227 * Remove the parent child relationships from all tabs.
228 */
229 void removeParentChildRelationShips() {
230 for (Tab tab : mTabs) {
231 tab.removeFromTree();
232 }
233 }
234
235 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800236 * Remove the tab from the list. If the tab is the current tab shown, the
237 * last created tab will be shown.
238 * @param t The tab to be removed.
239 */
240 boolean removeTab(Tab t) {
241 if (t == null) {
242 return false;
243 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700244
Patrick Scottd944d4d2010-01-27 16:39:11 -0500245 // Grab the current tab before modifying the list.
246 Tab current = getCurrentTab();
247
248 // Remove t from our list of tabs.
249 mTabs.remove(t);
250
251 // Put the tab in the background only if it is the current one.
252 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700253 t.putInBackground();
254 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500255 } else {
256 // If a tab that is earlier in the list gets removed, the current
257 // index no longer points to the correct tab.
Michael Kolbc831b632011-05-11 09:30:34 -0700258 mCurrentTab = getTabPosition(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800259 }
260
Grace Kloba22ac16e2009-10-07 18:00:23 -0700261 // destroy the tab
262 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800263 // clear it's references to parent and children
264 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800265
The Android Open Source Project0c908882009-03-03 19:32:16 -0800266 // Remove it from the queue of viewed tabs.
267 mTabQueue.remove(t);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800268 return true;
269 }
270
271 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800272 * Destroy all the tabs and subwindows
273 */
274 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800275 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700276 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800277 }
278 mTabs.clear();
279 mTabQueue.clear();
280 }
281
282 /**
283 * Returns the number of tabs created.
284 * @return The number of tabs created.
285 */
286 int getTabCount() {
287 return mTabs.size();
288 }
289
The Android Open Source Project0c908882009-03-03 19:32:16 -0800290 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700291 * save the tab state:
292 * current position
293 * position sorted array of tab ids
294 * for each tab id, save the tab state
295 * @param outState
John Reckaed9c542011-05-27 16:08:53 -0700296 * @param saveImages
The Android Open Source Project0c908882009-03-03 19:32:16 -0800297 */
John Reck1cf4b792011-07-26 10:22:22 -0700298 void saveState(Bundle outState) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800299 final int numTabs = getTabCount();
John Reck24f18262011-06-17 14:47:20 -0700300 if (numTabs == 0) {
301 return;
302 }
Michael Kolbc831b632011-05-11 09:30:34 -0700303 long[] ids = new long[numTabs];
304 int i = 0;
305 for (Tab tab : mTabs) {
John Reck1cf4b792011-07-26 10:22:22 -0700306 Bundle tabState = tab.saveState();
307 if (tabState != null) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700308 ids[i++] = tab.getId();
John Reck52be4782011-08-26 15:37:29 -0700309 String key = Long.toString(tab.getId());
310 if (outState.containsKey(key)) {
311 // Dump the tab state for debugging purposes
312 for (Tab dt : mTabs) {
313 Log.e(LOGTAG, dt.toString());
314 }
315 throw new IllegalStateException(
316 "Error saving state, duplicate tab ids!");
317 }
318 outState.putBundle(key, tabState);
Michael Kolb3ae7f742011-07-13 15:18:25 -0700319 } else {
320 ids[i++] = -1;
John Reck52be4782011-08-26 15:37:29 -0700321 // Since we won't be restoring the thumbnail, delete it
322 tab.deleteThumbnail();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800323 }
324 }
John Reck24f18262011-06-17 14:47:20 -0700325 if (!outState.isEmpty()) {
326 outState.putLongArray(POSITIONS, ids);
327 Tab current = getCurrentTab();
328 long cid = -1;
329 if (current != null) {
330 cid = current.getId();
331 }
332 outState.putLong(CURRENT, cid);
Michael Kolbdbf39812011-06-10 14:06:40 -0700333 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800334 }
335
336 /**
Patrick Scott7d50a932011-02-04 09:27:26 -0500337 * Check if the state can be restored. If the state can be restored, the
Michael Kolbc831b632011-05-11 09:30:34 -0700338 * current tab id is returned. This can be passed to restoreState below
Patrick Scott7d50a932011-02-04 09:27:26 -0500339 * in order to restore the correct tab. Otherwise, -1 is returned and the
340 * state cannot be restored.
341 */
Michael Kolbc831b632011-05-11 09:30:34 -0700342 long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
343 final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
344 if (ids == null) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500345 return -1;
346 }
Michael Kolbc831b632011-05-11 09:30:34 -0700347 final long oldcurrent = inState.getLong(CURRENT);
348 long current = -1;
Michael Kolb3ae7f742011-07-13 15:18:25 -0700349 if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
John Reck1cf4b792011-07-26 10:22:22 -0700350 current = oldcurrent;
Patrick Scott7d50a932011-02-04 09:27:26 -0500351 } else {
Michael Kolbc831b632011-05-11 09:30:34 -0700352 // pick first non incognito tab
353 for (long id : ids) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700354 if (hasState(id, inState) && !isIncognito(id, inState)) {
Michael Kolbc831b632011-05-11 09:30:34 -0700355 current = id;
Patrick Scott7d50a932011-02-04 09:27:26 -0500356 break;
357 }
358 }
359 }
Michael Kolbc831b632011-05-11 09:30:34 -0700360 return current;
Patrick Scott7d50a932011-02-04 09:27:26 -0500361 }
362
Michael Kolb3ae7f742011-07-13 15:18:25 -0700363 private boolean hasState(long id, Bundle state) {
364 if (id == -1) return false;
365 Bundle tab = state.getBundle(Long.toString(id));
366 return ((tab != null) && !tab.isEmpty());
367 }
368
369 private boolean isIncognito(long id, Bundle state) {
370 Bundle tabstate = state.getBundle(Long.toString(id));
371 if ((tabstate != null) && !tabstate.isEmpty()) {
372 return tabstate.getBoolean(Tab.INCOGNITO);
373 }
374 return false;
375 }
376
Patrick Scott7d50a932011-02-04 09:27:26 -0500377 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800378 * Restore the state of all the tabs.
Michael Kolbc831b632011-05-11 09:30:34 -0700379 * @param currentId The tab id to restore.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800380 * @param inState The saved state of all the tabs.
Michael Kolb1bf23132010-11-19 12:55:12 -0800381 * @param restoreIncognitoTabs Restoring private browsing tabs
382 * @param restoreAll All webviews get restored, not just the current tab
383 * (this does not override handling of incognito tabs)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800384 */
Michael Kolbc831b632011-05-11 09:30:34 -0700385 void restoreState(Bundle inState, long currentId,
Patrick Scott7d50a932011-02-04 09:27:26 -0500386 boolean restoreIncognitoTabs, boolean restoreAll) {
Michael Kolbc831b632011-05-11 09:30:34 -0700387 if (currentId == -1) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500388 return;
389 }
Michael Kolbc831b632011-05-11 09:30:34 -0700390 long[] ids = inState.getLongArray(POSITIONS);
391 long maxId = -Long.MAX_VALUE;
392 HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>();
393 for (long id : ids) {
394 if (id > maxId) {
395 maxId = id;
396 }
397 final String idkey = Long.toString(id);
398 Bundle state = inState.getBundle(idkey);
John Reckd8c74522011-06-14 08:45:00 -0700399 if (state == null || state.isEmpty()) {
400 // Skip tab
401 continue;
402 } else if (!restoreIncognitoTabs
Michael Kolbc831b632011-05-11 09:30:34 -0700403 && state.getBoolean(Tab.INCOGNITO)) {
404 // ignore tab
405 } else if (id == currentId || restoreAll) {
John Reck1cf4b792011-07-26 10:22:22 -0700406 Tab t = createNewTab(state, false);
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100407 if (t == null) {
408 // We could "break" at this point, but we want
409 // sNextId to be set correctly.
410 continue;
411 }
qqzhou8c5b0a32013-07-22 15:31:03 +0800412
413 // add for carrier homepage feature
414 // If the webview restore successfully, add javascript interface again.
415 WebView view = t.getWebView();
416 if (view != null) {
417 String browserRes = SystemProperties.get("persist.env.c.browser.resource",
418 "default");
419 if ("ct".equals(browserRes)) {
420 view.getSettings().setJavaScriptEnabled(true);
421 if (mController.getActivity() instanceof BrowserActivity) {
422 view.addJavascriptInterface(mController.getActivity(),
423 "default_homepage");
424 }
425 }
426 }
427
Michael Kolbc831b632011-05-11 09:30:34 -0700428 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500429 // Me must set the current tab before restoring the state
430 // so that all the client classes are set.
Michael Kolbc831b632011-05-11 09:30:34 -0700431 if (id == currentId) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500432 setCurrentTab(t);
433 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700434 } else {
Patrick Scott7d50a932011-02-04 09:27:26 -0500435 // Create a new tab and don't restore the state yet, add it
436 // to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700437 Tab t = new Tab(mController, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700438 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500439 mTabs.add(t);
440 // added the tab to the front as they are not current
441 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700442 }
Michael Kolbc831b632011-05-11 09:30:34 -0700443 }
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100444
445 // make sure that there is no id overlap between the restored
446 // and new tabs
447 sNextId = maxId + 1;
448
John Reckd8c74522011-06-14 08:45:00 -0700449 if (mCurrentTab == -1) {
450 if (getTabCount() > 0) {
451 setCurrentTab(getTab(0));
John Reckd8c74522011-06-14 08:45:00 -0700452 }
453 }
Michael Kolbc831b632011-05-11 09:30:34 -0700454 // restore parent/child relationships
455 for (long id : ids) {
456 final Tab tab = tabMap.get(id);
457 final Bundle b = inState.getBundle(Long.toString(id));
458 if ((b != null) && (tab != null)) {
459 final long parentId = b.getLong(Tab.PARENTTAB, -1);
460 if (parentId != -1) {
461 final Tab parent = tabMap.get(parentId);
Patrick Scott7d50a932011-02-04 09:27:26 -0500462 if (parent != null) {
Michael Kolbc831b632011-05-11 09:30:34 -0700463 parent.addChildTab(tab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800464 }
465 }
466 }
467 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800468 }
469
470 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700471 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800472 * WebView cache;
473 */
474 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700475 if (getTabCount() == 0) return;
476
Grace Klobaf56f68d2010-04-11 22:53:42 -0700477 // free the least frequently used background tabs
478 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
479 if (tabs.size() > 0) {
480 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
481 for (Tab t : tabs) {
482 // store the WebView's state.
483 t.saveState();
484 // destroy the tab
485 t.destroy();
486 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800487 return;
488 }
489
Derek Sollenberger4433d032009-06-10 15:37:21 -0400490 // free the WebView's unused memory (this includes the cache)
491 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800492 WebView view = getCurrentWebView();
493 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400494 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800495 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800496 }
497
Grace Klobaf56f68d2010-04-11 22:53:42 -0700498 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
499 Vector<Tab> tabsToGo = new Vector<Tab>();
500
Patrick Scott2a67de42009-08-31 09:48:37 -0400501 // Don't do anything if we only have 1 tab or if the current tab is
502 // null.
503 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700504 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800505 }
506
Grace Klobaf56f68d2010-04-11 22:53:42 -0700507 if (mTabQueue.size() == 0) {
508 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800509 }
510
Grace Klobaf56f68d2010-04-11 22:53:42 -0700511 // Rip through the queue starting at the beginning and tear down half of
512 // available tabs which are not the current tab or the parent of the
513 // current tab.
514 int openTabCount = 0;
515 for (Tab t : mTabQueue) {
516 if (t != null && t.getWebView() != null) {
517 openTabCount++;
Michael Kolbc831b632011-05-11 09:30:34 -0700518 if (t != current && t != current.getParent()) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700519 tabsToGo.add(t);
520 }
521 }
522 }
523
524 openTabCount /= 2;
525 if (tabsToGo.size() > openTabCount) {
526 tabsToGo.setSize(openTabCount);
527 }
528
529 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800530 }
531
Michael Kolbff6a7482011-07-26 16:37:15 -0700532 Tab getLeastUsedTab(Tab current) {
533 if (getTabCount() == 1 || current == null) {
534 return null;
535 }
536 if (mTabQueue.size() == 0) {
537 return null;
538 }
539 // find a tab which is not the current tab or the parent of the
540 // current tab
541 for (Tab t : mTabQueue) {
542 if (t != null && t.getWebView() != null) {
543 if (t != current && t != current.getParent()) {
544 return t;
545 }
546 }
547 }
548 return null;
549 }
550
The Android Open Source Project0c908882009-03-03 19:32:16 -0800551 /**
552 * Show the tab that contains the given WebView.
553 * @param view The WebView used to find the tab.
554 */
555 Tab getTabFromView(WebView view) {
Michael Kolbc831b632011-05-11 09:30:34 -0700556 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700557 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800558 return t;
559 }
560 }
561 return null;
562 }
563
564 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700565 * Return the tab with the matching application id.
566 * @param id The application identifier.
567 */
Michael Kolbc831b632011-05-11 09:30:34 -0700568 Tab getTabFromAppId(String id) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700569 if (id == null) {
570 return null;
571 }
Michael Kolbc831b632011-05-11 09:30:34 -0700572 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700573 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700574 return t;
575 }
576 }
577 return null;
578 }
579
Grace Kloba22ac16e2009-10-07 18:00:23 -0700580 /**
581 * Stop loading in all opened WebView including subWindows.
582 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700583 void stopAllLoading() {
Michael Kolbc831b632011-05-11 09:30:34 -0700584 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700585 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700586 if (webview != null) {
587 webview.stopLoading();
588 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700589 final WebView subview = t.getSubWebView();
590 if (subview != null) {
Mattias Nilsson9727af82012-06-14 17:07:56 +0200591 subview.stopLoading();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700592 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700593 }
594 }
595
John Reckdb22ec42011-06-29 11:31:24 -0700596 // This method checks if a tab matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400597 private boolean tabMatchesUrl(Tab t, String url) {
John Reckdb22ec42011-06-29 11:31:24 -0700598 return url.equals(t.getUrl()) || url.equals(t.getOriginalUrl());
Patrick Scottcd115892009-07-16 09:42:58 -0400599 }
600
601 /**
John Reckdb22ec42011-06-29 11:31:24 -0700602 * Return the tab that matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400603 * @param url The url to search for.
604 */
John Reckdb22ec42011-06-29 11:31:24 -0700605 Tab findTabWithUrl(String url) {
Patrick Scottcd115892009-07-16 09:42:58 -0400606 if (url == null) {
607 return null;
608 }
609 // Check the current tab first.
John Reckdb22ec42011-06-29 11:31:24 -0700610 Tab currentTab = getCurrentTab();
611 if (currentTab != null && tabMatchesUrl(currentTab, url)) {
612 return currentTab;
Patrick Scottcd115892009-07-16 09:42:58 -0400613 }
614 // Now check all the rest.
Michael Kolbc831b632011-05-11 09:30:34 -0700615 for (Tab tab : mTabs) {
616 if (tabMatchesUrl(tab, url)) {
John Reckdb22ec42011-06-29 11:31:24 -0700617 return tab;
Patrick Scottcd115892009-07-16 09:42:58 -0400618 }
619 }
620 return null;
621 }
622
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700623 /**
John Reck30c714c2010-12-16 17:30:34 -0800624 * Recreate the main WebView of the given tab.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700625 */
John Reck30c714c2010-12-16 17:30:34 -0800626 void recreateWebView(Tab t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700627 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700628 if (w != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700629 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700630 }
631 // Create a new WebView. If this tab is the current tab, we need to put
632 // back all the clients so force it to be the current tab.
Michael Kolb91911a22012-01-17 11:21:25 -0800633 t.setWebView(createNewWebView(), false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700634 if (getCurrentTab() == t) {
635 setCurrentTab(t, true);
636 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700637 }
638
639 /**
640 * Creates a new WebView and registers it with the global settings.
641 */
642 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700643 return createNewWebView(false);
644 }
645
646 /**
647 * Creates a new WebView and registers it with the global settings.
648 * @param privateBrowsing When true, enables private browsing in the new
649 * WebView.
650 */
651 private WebView createNewWebView(boolean privateBrowsing) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700652 return mController.getWebViewFactory().createWebView(privateBrowsing);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700653 }
654
655 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800656 * Put the current tab in the background and set newTab as the current tab.
657 * @param newTab The new tab. If newTab is null, the current tab is not
658 * set.
659 */
660 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700661 return setCurrentTab(newTab, false);
662 }
663
664 /**
665 * If force is true, this method skips the check for newTab == current.
666 */
667 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800668 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700669 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800670 return true;
671 }
672 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700673 current.putInBackground();
674 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800675 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800676 if (newTab == null) {
677 return false;
678 }
679
680 // Move the newTab to the end of the queue
681 int index = mTabQueue.indexOf(newTab);
682 if (index != -1) {
683 mTabQueue.remove(index);
684 }
685 mTabQueue.add(newTab);
686
The Android Open Source Project0c908882009-03-03 19:32:16 -0800687 // Display the new current tab
688 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700689 WebView mainView = newTab.getWebView();
John Reckd8c74522011-06-14 08:45:00 -0700690 boolean needRestore = !newTab.isSnapshot() && (mainView == null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800691 if (needRestore) {
692 // Same work as in createNewTab() except don't do new Tab()
Steve Block2bc69912009-07-30 14:45:13 +0100693 mainView = createNewWebView();
694 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800695 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700696 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800697 return true;
698 }
Michael Kolbfe251992010-07-08 15:41:55 -0700699
John Reck8ee633f2011-08-09 16:00:35 -0700700 public void setOnThumbnailUpdatedListener(OnThumbnailUpdatedListener listener) {
701 mOnThumbnailUpdatedListener = listener;
702 for (Tab t : mTabs) {
703 WebView web = t.getWebView();
704 if (web != null) {
705 web.setPictureListener(listener != null ? t : null);
706 }
707 }
708 }
709
710 public OnThumbnailUpdatedListener getOnThumbnailUpdatedListener() {
711 return mOnThumbnailUpdatedListener;
712 }
713
The Android Open Source Project0c908882009-03-03 19:32:16 -0800714}