blob: 66736cbe55e185f90a6d78e0a622b8e3525ea9e0 [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
Bijan Amirzada41242f22014-03-21 12:12:18 -070022import com.android.browser.reflect.ReflectHelper;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080023
24import org.codeaurora.swe.WebView;
The Android Open Source Project0c908882009-03-03 19:32:16 -080025
The Android Open Source Project0c908882009-03-03 19:32:16 -080026import java.util.ArrayList;
Elliott Slaughter3d6df162010-08-25 13:17:44 -070027import java.util.HashMap;
Michael Kolb1bf23132010-11-19 12:55:12 -080028import java.util.List;
The Android Open Source Project0c908882009-03-03 19:32:16 -080029import java.util.Vector;
30
31class TabControl {
32 // Log Tag
33 private static final String LOGTAG = "TabControl";
Michael Kolbc831b632011-05-11 09:30:34 -070034
John Reckd8c74522011-06-14 08:45:00 -070035 // next Tab ID, starting at 1
36 private static long sNextId = 1;
Michael Kolbc831b632011-05-11 09:30:34 -070037
38 private static final String POSITIONS = "positions";
39 private static final String CURRENT = "current";
40
John Reck8ee633f2011-08-09 16:00:35 -070041 public static interface OnThumbnailUpdatedListener {
42 void onThumbnailUpdated(Tab t);
43 }
44
The Android Open Source Project0c908882009-03-03 19:32:16 -080045 // Maximum number of tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070046 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080047 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070048 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080049 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070050 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080051 // Current position in mTabs.
52 private int mCurrentTab = -1;
Michael Kolb8233fac2010-10-26 16:08:53 -070053 // the main browser controller
54 private final Controller mController;
55
John Reck8ee633f2011-08-09 16:00:35 -070056 private OnThumbnailUpdatedListener mOnThumbnailUpdatedListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080057
58 /**
Michael Kolb8233fac2010-10-26 16:08:53 -070059 * Construct a new TabControl object
The Android Open Source Project0c908882009-03-03 19:32:16 -080060 */
Michael Kolb8233fac2010-10-26 16:08:53 -070061 TabControl(Controller controller) {
62 mController = controller;
Michael Kolb8233fac2010-10-26 16:08:53 -070063 mMaxTabs = mController.getMaxTabs();
Michael Kolb6e4653e2010-09-27 16:22:38 -070064 mTabs = new ArrayList<Tab>(mMaxTabs);
65 mTabQueue = new ArrayList<Tab>(mMaxTabs);
The Android Open Source Project0c908882009-03-03 19:32:16 -080066 }
67
John Reck52be4782011-08-26 15:37:29 -070068 synchronized static long getNextId() {
Michael Kolbc831b632011-05-11 09:30:34 -070069 return sNextId++;
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) {
Mathew Inwoode09305e2011-09-02 12:03:26 +0100176 for (Tab current : mTabs) {
177 if (current != null && current.getId() == tab.getId()) {
178 throw new IllegalStateException("Tab with id " + tab.getId() + " already exists: "
179 + current.toString());
180 }
181 }
Michael Kolb14612442011-06-24 13:06:29 -0700182 mTabs.add(tab);
183 tab.setController(mController);
184 mController.onSetWebView(tab, tab.getWebView());
185 tab.putInBackground();
186 }
187
Elliott Slaughtere440a882010-08-20 13:54:45 -0700188 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700189 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800190 * @return The newly createTab or null if we have reached the maximum
191 * number of open tabs.
192 */
Michael Kolb7bcafde2011-05-09 13:55:59 -0700193 Tab createNewTab(boolean privateBrowsing) {
John Reck1cf4b792011-07-26 10:22:22 -0700194 return createNewTab(null, privateBrowsing);
195 }
196
197 Tab createNewTab(Bundle state, boolean privateBrowsing) {
198 int size = mTabs.size();
199 // Return false if we have maxed out on tabs
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100200 if (!canCreateNewTab()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800201 return null;
202 }
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100203
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700204 final WebView w = createNewWebView(privateBrowsing);
Steve Block2bc69912009-07-30 14:45:13 +0100205
The Android Open Source Project0c908882009-03-03 19:32:16 -0800206 // Create a new tab and add it to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700207 Tab t = new Tab(mController, w, state);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800208 mTabs.add(t);
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700209 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700210 t.putInBackground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800211 return t;
212 }
213
214 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700215 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700216 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700217 */
218 Tab createNewTab() {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700219 return createNewTab(false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700220 }
221
John Reckd8c74522011-06-14 08:45:00 -0700222 SnapshotTab createSnapshotTab(long snapshotId) {
John Reckd8c74522011-06-14 08:45:00 -0700223 SnapshotTab t = new SnapshotTab(mController, snapshotId);
John Reckd8c74522011-06-14 08:45:00 -0700224 mTabs.add(t);
225 return t;
226 }
227
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700228 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500229 * Remove the parent child relationships from all tabs.
230 */
231 void removeParentChildRelationShips() {
232 for (Tab tab : mTabs) {
233 tab.removeFromTree();
234 }
235 }
236
237 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800238 * Remove the tab from the list. If the tab is the current tab shown, the
239 * last created tab will be shown.
240 * @param t The tab to be removed.
241 */
242 boolean removeTab(Tab t) {
243 if (t == null) {
244 return false;
245 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700246
Patrick Scottd944d4d2010-01-27 16:39:11 -0500247 // Grab the current tab before modifying the list.
248 Tab current = getCurrentTab();
249
250 // Remove t from our list of tabs.
251 mTabs.remove(t);
252
253 // Put the tab in the background only if it is the current one.
254 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700255 t.putInBackground();
256 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500257 } else {
258 // If a tab that is earlier in the list gets removed, the current
259 // index no longer points to the correct tab.
Michael Kolbc831b632011-05-11 09:30:34 -0700260 mCurrentTab = getTabPosition(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800261 }
262
Grace Kloba22ac16e2009-10-07 18:00:23 -0700263 // destroy the tab
264 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800265 // clear it's references to parent and children
266 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800267
The Android Open Source Project0c908882009-03-03 19:32:16 -0800268 // Remove it from the queue of viewed tabs.
269 mTabQueue.remove(t);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800270 return true;
271 }
272
273 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800274 * Destroy all the tabs and subwindows
275 */
276 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800277 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700278 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800279 }
280 mTabs.clear();
281 mTabQueue.clear();
282 }
283
284 /**
285 * Returns the number of tabs created.
286 * @return The number of tabs created.
287 */
288 int getTabCount() {
289 return mTabs.size();
290 }
291
The Android Open Source Project0c908882009-03-03 19:32:16 -0800292 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700293 * save the tab state:
294 * current position
295 * position sorted array of tab ids
296 * for each tab id, save the tab state
297 * @param outState
John Reckaed9c542011-05-27 16:08:53 -0700298 * @param saveImages
The Android Open Source Project0c908882009-03-03 19:32:16 -0800299 */
John Reck1cf4b792011-07-26 10:22:22 -0700300 void saveState(Bundle outState) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800301 final int numTabs = getTabCount();
John Reck24f18262011-06-17 14:47:20 -0700302 if (numTabs == 0) {
303 return;
304 }
Michael Kolbc831b632011-05-11 09:30:34 -0700305 long[] ids = new long[numTabs];
306 int i = 0;
307 for (Tab tab : mTabs) {
John Reck1cf4b792011-07-26 10:22:22 -0700308 Bundle tabState = tab.saveState();
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800309 if (tabState != null && tab.getWebView() != null
310 && tab.getWebView().isPrivateBrowsingEnabled() == false) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700311 ids[i++] = tab.getId();
John Reck52be4782011-08-26 15:37:29 -0700312 String key = Long.toString(tab.getId());
313 if (outState.containsKey(key)) {
314 // Dump the tab state for debugging purposes
315 for (Tab dt : mTabs) {
316 Log.e(LOGTAG, dt.toString());
317 }
318 throw new IllegalStateException(
319 "Error saving state, duplicate tab ids!");
320 }
321 outState.putBundle(key, tabState);
Michael Kolb3ae7f742011-07-13 15:18:25 -0700322 } else {
323 ids[i++] = -1;
John Reck52be4782011-08-26 15:37:29 -0700324 // Since we won't be restoring the thumbnail, delete it
325 tab.deleteThumbnail();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800326 }
327 }
John Reck24f18262011-06-17 14:47:20 -0700328 if (!outState.isEmpty()) {
329 outState.putLongArray(POSITIONS, ids);
330 Tab current = getCurrentTab();
331 long cid = -1;
332 if (current != null) {
333 cid = current.getId();
334 }
335 outState.putLong(CURRENT, cid);
Michael Kolbdbf39812011-06-10 14:06:40 -0700336 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800337 }
338
339 /**
Patrick Scott7d50a932011-02-04 09:27:26 -0500340 * Check if the state can be restored. If the state can be restored, the
Michael Kolbc831b632011-05-11 09:30:34 -0700341 * current tab id is returned. This can be passed to restoreState below
Patrick Scott7d50a932011-02-04 09:27:26 -0500342 * in order to restore the correct tab. Otherwise, -1 is returned and the
343 * state cannot be restored.
344 */
Michael Kolbc831b632011-05-11 09:30:34 -0700345 long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
346 final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
347 if (ids == null) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500348 return -1;
349 }
Michael Kolbc831b632011-05-11 09:30:34 -0700350 final long oldcurrent = inState.getLong(CURRENT);
351 long current = -1;
Michael Kolb3ae7f742011-07-13 15:18:25 -0700352 if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
John Reck1cf4b792011-07-26 10:22:22 -0700353 current = oldcurrent;
Patrick Scott7d50a932011-02-04 09:27:26 -0500354 } else {
Michael Kolbc831b632011-05-11 09:30:34 -0700355 // pick first non incognito tab
356 for (long id : ids) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700357 if (hasState(id, inState) && !isIncognito(id, inState)) {
Michael Kolbc831b632011-05-11 09:30:34 -0700358 current = id;
Patrick Scott7d50a932011-02-04 09:27:26 -0500359 break;
360 }
361 }
362 }
Michael Kolbc831b632011-05-11 09:30:34 -0700363 return current;
Patrick Scott7d50a932011-02-04 09:27:26 -0500364 }
365
Michael Kolb3ae7f742011-07-13 15:18:25 -0700366 private boolean hasState(long id, Bundle state) {
367 if (id == -1) return false;
368 Bundle tab = state.getBundle(Long.toString(id));
369 return ((tab != null) && !tab.isEmpty());
370 }
371
372 private boolean isIncognito(long id, Bundle state) {
373 Bundle tabstate = state.getBundle(Long.toString(id));
374 if ((tabstate != null) && !tabstate.isEmpty()) {
375 return tabstate.getBoolean(Tab.INCOGNITO);
376 }
377 return false;
378 }
379
Patrick Scott7d50a932011-02-04 09:27:26 -0500380 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800381 * Restore the state of all the tabs.
Michael Kolbc831b632011-05-11 09:30:34 -0700382 * @param currentId The tab id to restore.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800383 * @param inState The saved state of all the tabs.
Michael Kolb1bf23132010-11-19 12:55:12 -0800384 * @param restoreIncognitoTabs Restoring private browsing tabs
385 * @param restoreAll All webviews get restored, not just the current tab
386 * (this does not override handling of incognito tabs)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800387 */
Michael Kolbc831b632011-05-11 09:30:34 -0700388 void restoreState(Bundle inState, long currentId,
Patrick Scott7d50a932011-02-04 09:27:26 -0500389 boolean restoreIncognitoTabs, boolean restoreAll) {
Michael Kolbc831b632011-05-11 09:30:34 -0700390 if (currentId == -1) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500391 return;
392 }
Michael Kolbc831b632011-05-11 09:30:34 -0700393 long[] ids = inState.getLongArray(POSITIONS);
394 long maxId = -Long.MAX_VALUE;
395 HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>();
396 for (long id : ids) {
397 if (id > maxId) {
398 maxId = id;
399 }
400 final String idkey = Long.toString(id);
401 Bundle state = inState.getBundle(idkey);
John Reckd8c74522011-06-14 08:45:00 -0700402 if (state == null || state.isEmpty()) {
403 // Skip tab
404 continue;
405 } else if (!restoreIncognitoTabs
Michael Kolbc831b632011-05-11 09:30:34 -0700406 && state.getBoolean(Tab.INCOGNITO)) {
407 // ignore tab
408 } else if (id == currentId || restoreAll) {
John Reck1cf4b792011-07-26 10:22:22 -0700409 Tab t = createNewTab(state, false);
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100410 if (t == null) {
411 // We could "break" at this point, but we want
412 // sNextId to be set correctly.
413 continue;
414 }
qqzhou8c5b0a32013-07-22 15:31:03 +0800415
416 // add for carrier homepage feature
417 // If the webview restore successfully, add javascript interface again.
418 WebView view = t.getWebView();
419 if (view != null) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800420 Object[] params = { new String("persist.env.c.browser.resource"),
421 new String("default")};
422 Class[] type = new Class[] {String.class, String.class};
423 String browserRes = (String)ReflectHelper.invokeStaticMethod(
424 "android.os.SystemProperties","get",
425 type, params);
qqzhou8c5b0a32013-07-22 15:31:03 +0800426 if ("ct".equals(browserRes)) {
427 view.getSettings().setJavaScriptEnabled(true);
428 if (mController.getActivity() instanceof BrowserActivity) {
429 view.addJavascriptInterface(mController.getActivity(),
430 "default_homepage");
431 }
432 }
433 }
434
Michael Kolbc831b632011-05-11 09:30:34 -0700435 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500436 // Me must set the current tab before restoring the state
437 // so that all the client classes are set.
Michael Kolbc831b632011-05-11 09:30:34 -0700438 if (id == currentId) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500439 setCurrentTab(t);
440 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700441 } else {
Patrick Scott7d50a932011-02-04 09:27:26 -0500442 // Create a new tab and don't restore the state yet, add it
443 // to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700444 Tab t = new Tab(mController, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700445 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500446 mTabs.add(t);
447 // added the tab to the front as they are not current
448 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700449 }
Michael Kolbc831b632011-05-11 09:30:34 -0700450 }
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100451
452 // make sure that there is no id overlap between the restored
453 // and new tabs
454 sNextId = maxId + 1;
455
John Reckd8c74522011-06-14 08:45:00 -0700456 if (mCurrentTab == -1) {
457 if (getTabCount() > 0) {
458 setCurrentTab(getTab(0));
John Reckd8c74522011-06-14 08:45:00 -0700459 }
460 }
Michael Kolbc831b632011-05-11 09:30:34 -0700461 // restore parent/child relationships
462 for (long id : ids) {
463 final Tab tab = tabMap.get(id);
464 final Bundle b = inState.getBundle(Long.toString(id));
465 if ((b != null) && (tab != null)) {
466 final long parentId = b.getLong(Tab.PARENTTAB, -1);
467 if (parentId != -1) {
468 final Tab parent = tabMap.get(parentId);
Patrick Scott7d50a932011-02-04 09:27:26 -0500469 if (parent != null) {
Michael Kolbc831b632011-05-11 09:30:34 -0700470 parent.addChildTab(tab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800471 }
472 }
473 }
474 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800475 }
476
477 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700478 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800479 * WebView cache;
480 */
481 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700482 if (getTabCount() == 0) return;
483
Grace Klobaf56f68d2010-04-11 22:53:42 -0700484 // free the least frequently used background tabs
485 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
486 if (tabs.size() > 0) {
487 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
488 for (Tab t : tabs) {
489 // store the WebView's state.
490 t.saveState();
491 // destroy the tab
492 t.destroy();
493 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800494 return;
495 }
496
Derek Sollenberger4433d032009-06-10 15:37:21 -0400497 // free the WebView's unused memory (this includes the cache)
498 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800499 WebView view = getCurrentWebView();
500 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400501 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800502 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800503 }
504
Grace Klobaf56f68d2010-04-11 22:53:42 -0700505 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
506 Vector<Tab> tabsToGo = new Vector<Tab>();
507
Patrick Scott2a67de42009-08-31 09:48:37 -0400508 // Don't do anything if we only have 1 tab or if the current tab is
509 // null.
510 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700511 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800512 }
513
Grace Klobaf56f68d2010-04-11 22:53:42 -0700514 if (mTabQueue.size() == 0) {
515 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800516 }
517
Grace Klobaf56f68d2010-04-11 22:53:42 -0700518 // Rip through the queue starting at the beginning and tear down half of
519 // available tabs which are not the current tab or the parent of the
520 // current tab.
521 int openTabCount = 0;
522 for (Tab t : mTabQueue) {
523 if (t != null && t.getWebView() != null) {
524 openTabCount++;
Michael Kolbc831b632011-05-11 09:30:34 -0700525 if (t != current && t != current.getParent()) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700526 tabsToGo.add(t);
527 }
528 }
529 }
530
531 openTabCount /= 2;
532 if (tabsToGo.size() > openTabCount) {
533 tabsToGo.setSize(openTabCount);
534 }
535
536 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800537 }
538
Michael Kolbff6a7482011-07-26 16:37:15 -0700539 Tab getLeastUsedTab(Tab current) {
540 if (getTabCount() == 1 || current == null) {
541 return null;
542 }
543 if (mTabQueue.size() == 0) {
544 return null;
545 }
546 // find a tab which is not the current tab or the parent of the
547 // current tab
548 for (Tab t : mTabQueue) {
549 if (t != null && t.getWebView() != null) {
550 if (t != current && t != current.getParent()) {
551 return t;
552 }
553 }
554 }
555 return null;
556 }
557
The Android Open Source Project0c908882009-03-03 19:32:16 -0800558 /**
559 * Show the tab that contains the given WebView.
560 * @param view The WebView used to find the tab.
561 */
562 Tab getTabFromView(WebView view) {
Michael Kolbc831b632011-05-11 09:30:34 -0700563 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700564 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800565 return t;
566 }
567 }
568 return null;
569 }
570
571 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700572 * Return the tab with the matching application id.
573 * @param id The application identifier.
574 */
Michael Kolbc831b632011-05-11 09:30:34 -0700575 Tab getTabFromAppId(String id) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700576 if (id == null) {
577 return null;
578 }
Michael Kolbc831b632011-05-11 09:30:34 -0700579 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700580 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700581 return t;
582 }
583 }
584 return null;
585 }
586
Grace Kloba22ac16e2009-10-07 18:00:23 -0700587 /**
588 * Stop loading in all opened WebView including subWindows.
589 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700590 void stopAllLoading() {
Michael Kolbc831b632011-05-11 09:30:34 -0700591 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700592 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700593 if (webview != null) {
594 webview.stopLoading();
595 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700596 final WebView subview = t.getSubWebView();
597 if (subview != null) {
Mattias Nilsson9727af82012-06-14 17:07:56 +0200598 subview.stopLoading();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700599 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700600 }
601 }
602
John Reckdb22ec42011-06-29 11:31:24 -0700603 // This method checks if a tab matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400604 private boolean tabMatchesUrl(Tab t, String url) {
John Reckdb22ec42011-06-29 11:31:24 -0700605 return url.equals(t.getUrl()) || url.equals(t.getOriginalUrl());
Patrick Scottcd115892009-07-16 09:42:58 -0400606 }
607
608 /**
John Reckdb22ec42011-06-29 11:31:24 -0700609 * Return the tab that matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400610 * @param url The url to search for.
611 */
John Reckdb22ec42011-06-29 11:31:24 -0700612 Tab findTabWithUrl(String url) {
Patrick Scottcd115892009-07-16 09:42:58 -0400613 if (url == null) {
614 return null;
615 }
616 // Check the current tab first.
John Reckdb22ec42011-06-29 11:31:24 -0700617 Tab currentTab = getCurrentTab();
618 if (currentTab != null && tabMatchesUrl(currentTab, url)) {
619 return currentTab;
Patrick Scottcd115892009-07-16 09:42:58 -0400620 }
621 // Now check all the rest.
Michael Kolbc831b632011-05-11 09:30:34 -0700622 for (Tab tab : mTabs) {
623 if (tabMatchesUrl(tab, url)) {
John Reckdb22ec42011-06-29 11:31:24 -0700624 return tab;
Patrick Scottcd115892009-07-16 09:42:58 -0400625 }
626 }
627 return null;
628 }
629
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700630 /**
John Reck30c714c2010-12-16 17:30:34 -0800631 * Recreate the main WebView of the given tab.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700632 */
John Reck30c714c2010-12-16 17:30:34 -0800633 void recreateWebView(Tab t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700634 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700635 if (w != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700636 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700637 }
638 // Create a new WebView. If this tab is the current tab, we need to put
639 // back all the clients so force it to be the current tab.
Panos Thomasa9a5a582014-03-18 19:20:08 -0700640 t.setWebView(createNewWebView(t.isPrivateBrowsingEnabled()), false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700641 if (getCurrentTab() == t) {
642 setCurrentTab(t, true);
643 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700644 }
645
646 /**
647 * Creates a new WebView and registers it with the global settings.
648 */
649 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700650 return createNewWebView(false);
651 }
652
653 /**
654 * Creates a new WebView and registers it with the global settings.
655 * @param privateBrowsing When true, enables private browsing in the new
656 * WebView.
657 */
658 private WebView createNewWebView(boolean privateBrowsing) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700659 return mController.getWebViewFactory().createWebView(privateBrowsing);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700660 }
661
662 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800663 * Put the current tab in the background and set newTab as the current tab.
664 * @param newTab The new tab. If newTab is null, the current tab is not
665 * set.
666 */
667 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700668 return setCurrentTab(newTab, false);
669 }
670
671 /**
672 * If force is true, this method skips the check for newTab == current.
673 */
674 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800675 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700676 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800677 return true;
678 }
679 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700680 current.putInBackground();
681 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800682 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800683 if (newTab == null) {
684 return false;
685 }
686
687 // Move the newTab to the end of the queue
688 int index = mTabQueue.indexOf(newTab);
689 if (index != -1) {
690 mTabQueue.remove(index);
691 }
692 mTabQueue.add(newTab);
693
The Android Open Source Project0c908882009-03-03 19:32:16 -0800694 // Display the new current tab
695 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700696 WebView mainView = newTab.getWebView();
John Reckd8c74522011-06-14 08:45:00 -0700697 boolean needRestore = !newTab.isSnapshot() && (mainView == null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800698 if (needRestore) {
699 // Same work as in createNewTab() except don't do new Tab()
Panos Thomasa9a5a582014-03-18 19:20:08 -0700700 mainView = createNewWebView(newTab.isPrivateBrowsingEnabled());
Steve Block2bc69912009-07-30 14:45:13 +0100701 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800702 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700703 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800704 return true;
705 }
Michael Kolbfe251992010-07-08 15:41:55 -0700706
John Reck8ee633f2011-08-09 16:00:35 -0700707 public void setOnThumbnailUpdatedListener(OnThumbnailUpdatedListener listener) {
708 mOnThumbnailUpdatedListener = listener;
709 for (Tab t : mTabs) {
710 WebView web = t.getWebView();
711 if (web != null) {
712 web.setPictureListener(listener != null ? t : null);
713 }
714 }
715 }
716
717 public OnThumbnailUpdatedListener getOnThumbnailUpdatedListener() {
718 return mOnThumbnailUpdatedListener;
719 }
720
The Android Open Source Project0c908882009-03-03 19:32:16 -0800721}