blob: 33f60ff3bdc1105b219184562a74e47688c70e1a [file] [log] [blame]
The Android Open Source Project0c908882009-03-03 19:32:16 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
The Android Open Source Project0c908882009-03-03 19:32:16 -080018
The Android Open Source Project0c908882009-03-03 19:32:16 -080019import android.os.Bundle;
The Android Open Source Project0c908882009-03-03 19:32:16 -080020import android.util.Log;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080021
Axesh R. Ajmera2e241242014-05-19 15:53:38 -070022import org.codeaurora.swe.GeolocationPermissions;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080023import org.codeaurora.swe.WebView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080024
The Android Open Source Project0c908882009-03-03 19:32:16 -080025import java.util.ArrayList;
Elliott Slaughter3d6df162010-08-25 13:17:44 -070026import java.util.HashMap;
Michael Kolb1bf23132010-11-19 12:55:12 -080027import java.util.List;
The Android Open Source Project0c908882009-03-03 19:32:16 -080028import java.util.Vector;
29
30class TabControl {
31 // Log Tag
32 private static final String LOGTAG = "TabControl";
Michael Kolbc831b632011-05-11 09:30:34 -070033
John Reckd8c74522011-06-14 08:45:00 -070034 // next Tab ID, starting at 1
35 private static long sNextId = 1;
Michael Kolbc831b632011-05-11 09:30:34 -070036
37 private static final String POSITIONS = "positions";
38 private static final String CURRENT = "current";
39
John Reck8ee633f2011-08-09 16:00:35 -070040 public static interface OnThumbnailUpdatedListener {
41 void onThumbnailUpdated(Tab t);
42 }
43
The Android Open Source Project0c908882009-03-03 19:32:16 -080044 // Maximum number of tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070045 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080046 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070047 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080048 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070049 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080050 // Current position in mTabs.
51 private int mCurrentTab = -1;
Michael Kolb8233fac2010-10-26 16:08:53 -070052 // the main browser controller
53 private final Controller mController;
Axesh R. Ajmera2e241242014-05-19 15:53:38 -070054 // number of incognito tabs
55 private int mNumIncognito = 0;
Michael Kolb8233fac2010-10-26 16:08:53 -070056
John Reck8ee633f2011-08-09 16:00:35 -070057 private OnThumbnailUpdatedListener mOnThumbnailUpdatedListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080058
59 /**
Michael Kolb8233fac2010-10-26 16:08:53 -070060 * Construct a new TabControl object
The Android Open Source Project0c908882009-03-03 19:32:16 -080061 */
Michael Kolb8233fac2010-10-26 16:08:53 -070062 TabControl(Controller controller) {
63 mController = controller;
Michael Kolb8233fac2010-10-26 16:08:53 -070064 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
John Reck52be4782011-08-26 15:37:29 -070069 synchronized static long getNextId() {
Michael Kolbc831b632011-05-11 09:30:34 -070070 return sNextId++;
71 }
72
Michael Kolb68775752010-08-19 12:42:01 -070073 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080074 * Return the current tab's main WebView. This will always return the main
75 * WebView for a given tab and not a subwindow.
76 * @return The current tab's WebView.
77 */
78 WebView getCurrentWebView() {
79 Tab t = getTab(mCurrentTab);
80 if (t == null) {
81 return null;
82 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070083 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +010084 }
85
86 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -080087 * Return the current tab's top-level WebView. This can return a subwindow
88 * if one exists.
89 * @return The top-level WebView of the current tab.
90 */
91 WebView getCurrentTopWebView() {
92 Tab t = getTab(mCurrentTab);
93 if (t == null) {
94 return null;
95 }
Grace Kloba22ac16e2009-10-07 18:00:23 -070096 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -080097 }
98
99 /**
100 * Return the current tab's subwindow if it exists.
101 * @return The subwindow of the current tab or null if it doesn't exist.
102 */
103 WebView getCurrentSubWindow() {
104 Tab t = getTab(mCurrentTab);
105 if (t == null) {
106 return null;
107 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700108 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800109 }
110
111 /**
Michael Kolb1bf23132010-11-19 12:55:12 -0800112 * return the list of tabs
113 */
114 List<Tab> getTabs() {
115 return mTabs;
116 }
117
118 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700119 * Return the tab at the specified position.
120 * @return The Tab for the specified position or null if the tab does not
The Android Open Source Project0c908882009-03-03 19:32:16 -0800121 * exist.
122 */
Michael Kolbc831b632011-05-11 09:30:34 -0700123 Tab getTab(int position) {
124 if (position >= 0 && position < mTabs.size()) {
125 return mTabs.get(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800126 }
127 return null;
128 }
129
130 /**
131 * Return the current tab.
132 * @return The current tab.
133 */
134 Tab getCurrentTab() {
135 return getTab(mCurrentTab);
136 }
137
138 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700139 * Return the current tab position.
140 * @return The current tab position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800141 */
Michael Kolbc831b632011-05-11 09:30:34 -0700142 int getCurrentPosition() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800143 return mCurrentTab;
144 }
Michael Kolbfe251992010-07-08 15:41:55 -0700145
The Android Open Source Project0c908882009-03-03 19:32:16 -0800146 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700147 * Given a Tab, find it's position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800148 * @param Tab to find
Michael Kolbc831b632011-05-11 09:30:34 -0700149 * @return position of Tab or -1 if not found
The Android Open Source Project0c908882009-03-03 19:32:16 -0800150 */
Michael Kolbc831b632011-05-11 09:30:34 -0700151 int getTabPosition(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400152 if (tab == null) {
153 return -1;
154 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800155 return mTabs.indexOf(tab);
156 }
157
Grace Kloba22ac16e2009-10-07 18:00:23 -0700158 boolean canCreateNewTab() {
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100159 return mMaxTabs > mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700160 }
161
The Android Open Source Project0c908882009-03-03 19:32:16 -0800162 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700163 * Returns true if there are any incognito tabs open.
164 * @return True when any incognito tabs are open, false otherwise.
165 */
166 boolean hasAnyOpenIncognitoTabs() {
167 for (Tab tab : mTabs) {
Michael Kolbc831b632011-05-11 09:30:34 -0700168 if (tab.getWebView() != null
169 && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700170 return true;
171 }
172 }
173 return false;
174 }
175
Michael Kolb14612442011-06-24 13:06:29 -0700176 void addPreloadedTab(Tab tab) {
Mathew Inwoode09305e2011-09-02 12:03:26 +0100177 for (Tab current : mTabs) {
178 if (current != null && current.getId() == tab.getId()) {
179 throw new IllegalStateException("Tab with id " + tab.getId() + " already exists: "
180 + current.toString());
181 }
182 }
Michael Kolb14612442011-06-24 13:06:29 -0700183 mTabs.add(tab);
184 tab.setController(mController);
185 mController.onSetWebView(tab, tab.getWebView());
186 tab.putInBackground();
187 }
188
Elliott Slaughtere440a882010-08-20 13:54:45 -0700189 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700190 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800191 * @return The newly createTab or null if we have reached the maximum
192 * number of open tabs.
193 */
Michael Kolb7bcafde2011-05-09 13:55:59 -0700194 Tab createNewTab(boolean privateBrowsing) {
John Reck1cf4b792011-07-26 10:22:22 -0700195 return createNewTab(null, privateBrowsing);
196 }
197
198 Tab createNewTab(Bundle state, boolean privateBrowsing) {
199 int size = mTabs.size();
200 // Return false if we have maxed out on tabs
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100201 if (!canCreateNewTab()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800202 return null;
203 }
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100204
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700205 final WebView w = createNewWebView(privateBrowsing);
Steve Block2bc69912009-07-30 14:45:13 +0100206
The Android Open Source Project0c908882009-03-03 19:32:16 -0800207 // Create a new tab and add it to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700208 Tab t = new Tab(mController, w, state);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800209 mTabs.add(t);
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700210 if (privateBrowsing) {
211 mNumIncognito += 1;
212 }
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700213 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700214 t.putInBackground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800215 return t;
216 }
217
218 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700219 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700220 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700221 */
222 Tab createNewTab() {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700223 return createNewTab(false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700224 }
225
John Reckd8c74522011-06-14 08:45:00 -0700226 SnapshotTab createSnapshotTab(long snapshotId) {
John Reckd8c74522011-06-14 08:45:00 -0700227 SnapshotTab t = new SnapshotTab(mController, snapshotId);
John Reckd8c74522011-06-14 08:45:00 -0700228 mTabs.add(t);
229 return t;
230 }
231
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700232 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500233 * Remove the parent child relationships from all tabs.
234 */
235 void removeParentChildRelationShips() {
236 for (Tab tab : mTabs) {
237 tab.removeFromTree();
238 }
239 }
240
241 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800242 * Remove the tab from the list. If the tab is the current tab shown, the
243 * last created tab will be shown.
244 * @param t The tab to be removed.
245 */
246 boolean removeTab(Tab t) {
247 if (t == null) {
248 return false;
249 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700250
Patrick Scottd944d4d2010-01-27 16:39:11 -0500251 // Grab the current tab before modifying the list.
252 Tab current = getCurrentTab();
253
254 // Remove t from our list of tabs.
255 mTabs.remove(t);
256
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700257 //Clear incognito geolocation state if this is the last incognito tab.
258 if (t.isPrivateBrowsingEnabled()) {
259 mNumIncognito -= 1;
260 if (mNumIncognito == 0) {
261 GeolocationPermissions.onIncognitoTabsRemoved();
262 }
263 }
264
Patrick Scottd944d4d2010-01-27 16:39:11 -0500265 // Put the tab in the background only if it is the current one.
266 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700267 t.putInBackground();
268 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500269 } else {
270 // If a tab that is earlier in the list gets removed, the current
271 // index no longer points to the correct tab.
Michael Kolbc831b632011-05-11 09:30:34 -0700272 mCurrentTab = getTabPosition(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800273 }
274
Grace Kloba22ac16e2009-10-07 18:00:23 -0700275 // destroy the tab
276 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800277 // clear it's references to parent and children
278 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800279
The Android Open Source Project0c908882009-03-03 19:32:16 -0800280 // Remove it from the queue of viewed tabs.
281 mTabQueue.remove(t);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800282 return true;
283 }
284
285 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800286 * Destroy all the tabs and subwindows
287 */
288 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800289 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700290 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800291 }
292 mTabs.clear();
293 mTabQueue.clear();
294 }
295
296 /**
297 * Returns the number of tabs created.
298 * @return The number of tabs created.
299 */
300 int getTabCount() {
301 return mTabs.size();
302 }
303
The Android Open Source Project0c908882009-03-03 19:32:16 -0800304 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700305 * save the tab state:
306 * current position
307 * position sorted array of tab ids
308 * for each tab id, save the tab state
309 * @param outState
John Reckaed9c542011-05-27 16:08:53 -0700310 * @param saveImages
The Android Open Source Project0c908882009-03-03 19:32:16 -0800311 */
John Reck1cf4b792011-07-26 10:22:22 -0700312 void saveState(Bundle outState) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800313 final int numTabs = getTabCount();
John Reck24f18262011-06-17 14:47:20 -0700314 if (numTabs == 0) {
315 return;
316 }
Michael Kolbc831b632011-05-11 09:30:34 -0700317 long[] ids = new long[numTabs];
318 int i = 0;
319 for (Tab tab : mTabs) {
John Reck1cf4b792011-07-26 10:22:22 -0700320 Bundle tabState = tab.saveState();
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800321 if (tabState != null && tab.getWebView() != null
322 && tab.getWebView().isPrivateBrowsingEnabled() == false) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700323 ids[i++] = tab.getId();
John Reck52be4782011-08-26 15:37:29 -0700324 String key = Long.toString(tab.getId());
325 if (outState.containsKey(key)) {
326 // Dump the tab state for debugging purposes
327 for (Tab dt : mTabs) {
328 Log.e(LOGTAG, dt.toString());
329 }
330 throw new IllegalStateException(
331 "Error saving state, duplicate tab ids!");
332 }
333 outState.putBundle(key, tabState);
Michael Kolb3ae7f742011-07-13 15:18:25 -0700334 } else {
335 ids[i++] = -1;
John Reck52be4782011-08-26 15:37:29 -0700336 // Since we won't be restoring the thumbnail, delete it
337 tab.deleteThumbnail();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800338 }
339 }
John Reck24f18262011-06-17 14:47:20 -0700340 if (!outState.isEmpty()) {
341 outState.putLongArray(POSITIONS, ids);
342 Tab current = getCurrentTab();
343 long cid = -1;
344 if (current != null) {
345 cid = current.getId();
346 }
347 outState.putLong(CURRENT, cid);
Michael Kolbdbf39812011-06-10 14:06:40 -0700348 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800349 }
350
351 /**
Patrick Scott7d50a932011-02-04 09:27:26 -0500352 * Check if the state can be restored. If the state can be restored, the
Michael Kolbc831b632011-05-11 09:30:34 -0700353 * current tab id is returned. This can be passed to restoreState below
Patrick Scott7d50a932011-02-04 09:27:26 -0500354 * in order to restore the correct tab. Otherwise, -1 is returned and the
355 * state cannot be restored.
356 */
Michael Kolbc831b632011-05-11 09:30:34 -0700357 long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
358 final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
359 if (ids == null) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500360 return -1;
361 }
Michael Kolbc831b632011-05-11 09:30:34 -0700362 final long oldcurrent = inState.getLong(CURRENT);
363 long current = -1;
Michael Kolb3ae7f742011-07-13 15:18:25 -0700364 if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
John Reck1cf4b792011-07-26 10:22:22 -0700365 current = oldcurrent;
Patrick Scott7d50a932011-02-04 09:27:26 -0500366 } else {
Michael Kolbc831b632011-05-11 09:30:34 -0700367 // pick first non incognito tab
368 for (long id : ids) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700369 if (hasState(id, inState) && !isIncognito(id, inState)) {
Michael Kolbc831b632011-05-11 09:30:34 -0700370 current = id;
Patrick Scott7d50a932011-02-04 09:27:26 -0500371 break;
372 }
373 }
374 }
Michael Kolbc831b632011-05-11 09:30:34 -0700375 return current;
Patrick Scott7d50a932011-02-04 09:27:26 -0500376 }
377
Michael Kolb3ae7f742011-07-13 15:18:25 -0700378 private boolean hasState(long id, Bundle state) {
379 if (id == -1) return false;
380 Bundle tab = state.getBundle(Long.toString(id));
381 return ((tab != null) && !tab.isEmpty());
382 }
383
384 private boolean isIncognito(long id, Bundle state) {
385 Bundle tabstate = state.getBundle(Long.toString(id));
386 if ((tabstate != null) && !tabstate.isEmpty()) {
387 return tabstate.getBoolean(Tab.INCOGNITO);
388 }
389 return false;
390 }
391
Patrick Scott7d50a932011-02-04 09:27:26 -0500392 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800393 * Restore the state of all the tabs.
Michael Kolbc831b632011-05-11 09:30:34 -0700394 * @param currentId The tab id to restore.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800395 * @param inState The saved state of all the tabs.
Michael Kolb1bf23132010-11-19 12:55:12 -0800396 * @param restoreIncognitoTabs Restoring private browsing tabs
397 * @param restoreAll All webviews get restored, not just the current tab
398 * (this does not override handling of incognito tabs)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800399 */
Michael Kolbc831b632011-05-11 09:30:34 -0700400 void restoreState(Bundle inState, long currentId,
Patrick Scott7d50a932011-02-04 09:27:26 -0500401 boolean restoreIncognitoTabs, boolean restoreAll) {
Michael Kolbc831b632011-05-11 09:30:34 -0700402 if (currentId == -1) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500403 return;
404 }
Michael Kolbc831b632011-05-11 09:30:34 -0700405 long[] ids = inState.getLongArray(POSITIONS);
406 long maxId = -Long.MAX_VALUE;
407 HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>();
408 for (long id : ids) {
409 if (id > maxId) {
410 maxId = id;
411 }
412 final String idkey = Long.toString(id);
413 Bundle state = inState.getBundle(idkey);
John Reckd8c74522011-06-14 08:45:00 -0700414 if (state == null || state.isEmpty()) {
415 // Skip tab
416 continue;
417 } else if (!restoreIncognitoTabs
Michael Kolbc831b632011-05-11 09:30:34 -0700418 && state.getBoolean(Tab.INCOGNITO)) {
419 // ignore tab
420 } else if (id == currentId || restoreAll) {
John Reck1cf4b792011-07-26 10:22:22 -0700421 Tab t = createNewTab(state, false);
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100422 if (t == null) {
423 // We could "break" at this point, but we want
424 // sNextId to be set correctly.
425 continue;
426 }
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700427 //handle restored pages that may require a JS interface
428 t.handleJsInterface(t.getWebView(), t.getUrl());
429
Michael Kolbc831b632011-05-11 09:30:34 -0700430 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500431 // Me must set the current tab before restoring the state
432 // so that all the client classes are set.
Michael Kolbc831b632011-05-11 09:30:34 -0700433 if (id == currentId) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500434 setCurrentTab(t);
435 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700436 } else {
Patrick Scott7d50a932011-02-04 09:27:26 -0500437 // Create a new tab and don't restore the state yet, add it
438 // to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700439 Tab t = new Tab(mController, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700440 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500441 mTabs.add(t);
442 // added the tab to the front as they are not current
443 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700444 }
Michael Kolbc831b632011-05-11 09:30:34 -0700445 }
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100446
447 // make sure that there is no id overlap between the restored
448 // and new tabs
449 sNextId = maxId + 1;
450
John Reckd8c74522011-06-14 08:45:00 -0700451 if (mCurrentTab == -1) {
452 if (getTabCount() > 0) {
453 setCurrentTab(getTab(0));
John Reckd8c74522011-06-14 08:45:00 -0700454 }
455 }
Michael Kolbc831b632011-05-11 09:30:34 -0700456 // restore parent/child relationships
457 for (long id : ids) {
458 final Tab tab = tabMap.get(id);
459 final Bundle b = inState.getBundle(Long.toString(id));
460 if ((b != null) && (tab != null)) {
461 final long parentId = b.getLong(Tab.PARENTTAB, -1);
462 if (parentId != -1) {
463 final Tab parent = tabMap.get(parentId);
Patrick Scott7d50a932011-02-04 09:27:26 -0500464 if (parent != null) {
Michael Kolbc831b632011-05-11 09:30:34 -0700465 parent.addChildTab(tab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800466 }
467 }
468 }
469 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800470 }
471
472 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700473 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800474 * WebView cache;
475 */
476 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700477 if (getTabCount() == 0) return;
478
Grace Klobaf56f68d2010-04-11 22:53:42 -0700479 // free the least frequently used background tabs
480 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
481 if (tabs.size() > 0) {
482 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
483 for (Tab t : tabs) {
484 // store the WebView's state.
485 t.saveState();
486 // destroy the tab
487 t.destroy();
488 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800489 return;
490 }
491
Derek Sollenberger4433d032009-06-10 15:37:21 -0400492 // free the WebView's unused memory (this includes the cache)
493 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800494 WebView view = getCurrentWebView();
495 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400496 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800497 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800498 }
499
Grace Klobaf56f68d2010-04-11 22:53:42 -0700500 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
501 Vector<Tab> tabsToGo = new Vector<Tab>();
502
Patrick Scott2a67de42009-08-31 09:48:37 -0400503 // Don't do anything if we only have 1 tab or if the current tab is
504 // null.
505 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700506 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800507 }
508
Grace Klobaf56f68d2010-04-11 22:53:42 -0700509 if (mTabQueue.size() == 0) {
510 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800511 }
512
Grace Klobaf56f68d2010-04-11 22:53:42 -0700513 // Rip through the queue starting at the beginning and tear down half of
514 // available tabs which are not the current tab or the parent of the
515 // current tab.
516 int openTabCount = 0;
517 for (Tab t : mTabQueue) {
518 if (t != null && t.getWebView() != null) {
519 openTabCount++;
Michael Kolbc831b632011-05-11 09:30:34 -0700520 if (t != current && t != current.getParent()) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700521 tabsToGo.add(t);
522 }
523 }
524 }
525
526 openTabCount /= 2;
527 if (tabsToGo.size() > openTabCount) {
528 tabsToGo.setSize(openTabCount);
529 }
530
531 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800532 }
533
Michael Kolbff6a7482011-07-26 16:37:15 -0700534 Tab getLeastUsedTab(Tab current) {
535 if (getTabCount() == 1 || current == null) {
536 return null;
537 }
538 if (mTabQueue.size() == 0) {
539 return null;
540 }
541 // find a tab which is not the current tab or the parent of the
542 // current tab
543 for (Tab t : mTabQueue) {
544 if (t != null && t.getWebView() != null) {
545 if (t != current && t != current.getParent()) {
546 return t;
547 }
548 }
549 }
550 return null;
551 }
552
The Android Open Source Project0c908882009-03-03 19:32:16 -0800553 /**
554 * Show the tab that contains the given WebView.
555 * @param view The WebView used to find the tab.
556 */
557 Tab getTabFromView(WebView view) {
Michael Kolbc831b632011-05-11 09:30:34 -0700558 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700559 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800560 return t;
561 }
562 }
563 return null;
564 }
565
566 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700567 * Return the tab with the matching application id.
568 * @param id The application identifier.
569 */
Michael Kolbc831b632011-05-11 09:30:34 -0700570 Tab getTabFromAppId(String id) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700571 if (id == null) {
572 return null;
573 }
Michael Kolbc831b632011-05-11 09:30:34 -0700574 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700575 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700576 return t;
577 }
578 }
579 return null;
580 }
581
Grace Kloba22ac16e2009-10-07 18:00:23 -0700582 /**
583 * Stop loading in all opened WebView including subWindows.
584 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700585 void stopAllLoading() {
Michael Kolbc831b632011-05-11 09:30:34 -0700586 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700587 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700588 if (webview != null) {
589 webview.stopLoading();
590 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700591 final WebView subview = t.getSubWebView();
592 if (subview != null) {
Mattias Nilsson9727af82012-06-14 17:07:56 +0200593 subview.stopLoading();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700594 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700595 }
596 }
597
John Reckdb22ec42011-06-29 11:31:24 -0700598 // This method checks if a tab matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400599 private boolean tabMatchesUrl(Tab t, String url) {
John Reckdb22ec42011-06-29 11:31:24 -0700600 return url.equals(t.getUrl()) || url.equals(t.getOriginalUrl());
Patrick Scottcd115892009-07-16 09:42:58 -0400601 }
602
603 /**
John Reckdb22ec42011-06-29 11:31:24 -0700604 * Return the tab that matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400605 * @param url The url to search for.
606 */
John Reckdb22ec42011-06-29 11:31:24 -0700607 Tab findTabWithUrl(String url) {
Patrick Scottcd115892009-07-16 09:42:58 -0400608 if (url == null) {
609 return null;
610 }
611 // Check the current tab first.
John Reckdb22ec42011-06-29 11:31:24 -0700612 Tab currentTab = getCurrentTab();
613 if (currentTab != null && tabMatchesUrl(currentTab, url)) {
614 return currentTab;
Patrick Scottcd115892009-07-16 09:42:58 -0400615 }
616 // Now check all the rest.
Michael Kolbc831b632011-05-11 09:30:34 -0700617 for (Tab tab : mTabs) {
618 if (tabMatchesUrl(tab, url)) {
John Reckdb22ec42011-06-29 11:31:24 -0700619 return tab;
Patrick Scottcd115892009-07-16 09:42:58 -0400620 }
621 }
622 return null;
623 }
624
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700625 /**
John Reck30c714c2010-12-16 17:30:34 -0800626 * Recreate the main WebView of the given tab.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700627 */
John Reck30c714c2010-12-16 17:30:34 -0800628 void recreateWebView(Tab t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700629 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700630 if (w != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700631 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700632 }
633 // Create a new WebView. If this tab is the current tab, we need to put
634 // back all the clients so force it to be the current tab.
Panos Thomasa9a5a582014-03-18 19:20:08 -0700635 t.setWebView(createNewWebView(t.isPrivateBrowsingEnabled()), false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700636 if (getCurrentTab() == t) {
637 setCurrentTab(t, true);
638 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700639 }
640
641 /**
642 * Creates a new WebView and registers it with the global settings.
643 */
644 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700645 return createNewWebView(false);
646 }
647
648 /**
649 * Creates a new WebView and registers it with the global settings.
650 * @param privateBrowsing When true, enables private browsing in the new
651 * WebView.
652 */
653 private WebView createNewWebView(boolean privateBrowsing) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700654 return mController.getWebViewFactory().createWebView(privateBrowsing);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700655 }
656
657 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800658 * Put the current tab in the background and set newTab as the current tab.
659 * @param newTab The new tab. If newTab is null, the current tab is not
660 * set.
661 */
662 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700663 return setCurrentTab(newTab, false);
664 }
665
666 /**
667 * If force is true, this method skips the check for newTab == current.
668 */
669 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800670 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700671 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800672 return true;
673 }
674 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700675 current.putInBackground();
676 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800677 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800678 if (newTab == null) {
679 return false;
680 }
681
682 // Move the newTab to the end of the queue
683 int index = mTabQueue.indexOf(newTab);
684 if (index != -1) {
685 mTabQueue.remove(index);
686 }
687 mTabQueue.add(newTab);
688
The Android Open Source Project0c908882009-03-03 19:32:16 -0800689 // Display the new current tab
690 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700691 WebView mainView = newTab.getWebView();
John Reckd8c74522011-06-14 08:45:00 -0700692 boolean needRestore = !newTab.isSnapshot() && (mainView == null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800693 if (needRestore) {
694 // Same work as in createNewTab() except don't do new Tab()
Panos Thomasa9a5a582014-03-18 19:20:08 -0700695 mainView = createNewWebView(newTab.isPrivateBrowsingEnabled());
Steve Block2bc69912009-07-30 14:45:13 +0100696 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800697 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700698 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800699 return true;
700 }
Michael Kolbfe251992010-07-08 15:41:55 -0700701
John Reck8ee633f2011-08-09 16:00:35 -0700702 public void setOnThumbnailUpdatedListener(OnThumbnailUpdatedListener listener) {
703 mOnThumbnailUpdatedListener = listener;
704 for (Tab t : mTabs) {
705 WebView web = t.getWebView();
706 if (web != null) {
707 web.setPictureListener(listener != null ? t : null);
708 }
709 }
710 }
711
712 public OnThumbnailUpdatedListener getOnThumbnailUpdatedListener() {
713 return mOnThumbnailUpdatedListener;
714 }
715
The Android Open Source Project0c908882009-03-03 19:32:16 -0800716}