blob: 91d2bca96175ee58ba7c030d2660e257374ae289 [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
Pankaj Garg75273bb2015-08-20 11:43:49 -070019import android.net.Uri;
The Android Open Source Project0c908882009-03-03 19:32:16 -080020import android.os.Bundle;
The Android Open Source Project0c908882009-03-03 19:32:16 -080021import android.util.Log;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080022
Axesh R. Ajmera2e241242014-05-19 15:53:38 -070023import org.codeaurora.swe.GeolocationPermissions;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080024import org.codeaurora.swe.WebView;
Enrico Ros1f5a0952014-11-18 20:15:48 -080025import org.codeaurora.swe.util.Observable;
The Android Open Source Project0c908882009-03-03 19:32:16 -080026
The Android Open Source Project0c908882009-03-03 19:32:16 -080027import java.util.ArrayList;
Elliott Slaughter3d6df162010-08-25 13:17:44 -070028import java.util.HashMap;
Michael Kolb1bf23132010-11-19 12:55:12 -080029import java.util.List;
The Android Open Source Project0c908882009-03-03 19:32:16 -080030import java.util.Vector;
31
32class TabControl {
33 // Log Tag
34 private static final String LOGTAG = "TabControl";
Michael Kolbc831b632011-05-11 09:30:34 -070035
John Reckd8c74522011-06-14 08:45:00 -070036 // next Tab ID, starting at 1
37 private static long sNextId = 1;
Michael Kolbc831b632011-05-11 09:30:34 -070038
39 private static final String POSITIONS = "positions";
40 private static final String CURRENT = "current";
41
Pankaj Garg75273bb2015-08-20 11:43:49 -070042
43 /*
44 Find and reload any live tabs that have loaded the given URL.
45 Note - Upto 2 tabs are live at any given moment.
46 */
47 public void findAndReload(String origin) {
48 for (Tab tab : mTabs){
49 if (tab.getWebView() != null) {
50 Uri url = Uri.parse(tab.getWebView().getUrl());
51 if (url.getHost().equals(origin)){
52 tab.getWebView().reload();
53 }
54 }
55 }
56 }
57
58 // Reload the all the live tabs
59 public void reloadLiveTabs() {
60 for (Tab tab : mTabs) {
61 if (tab.getWebView() != null) {
62 tab.getWebView().reload();
63 }
64 }
65 }
66
John Reck8ee633f2011-08-09 16:00:35 -070067 public static interface OnThumbnailUpdatedListener {
68 void onThumbnailUpdated(Tab t);
69 }
70
The Android Open Source Project0c908882009-03-03 19:32:16 -080071 // Maximum number of tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070072 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080073 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070074 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080075 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070076 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080077 // Current position in mTabs.
78 private int mCurrentTab = -1;
Michael Kolb8233fac2010-10-26 16:08:53 -070079 // the main browser controller
80 private final Controller mController;
Axesh R. Ajmera2e241242014-05-19 15:53:38 -070081 // number of incognito tabs
82 private int mNumIncognito = 0;
Michael Kolb8233fac2010-10-26 16:08:53 -070083
John Reck8ee633f2011-08-09 16:00:35 -070084 private OnThumbnailUpdatedListener mOnThumbnailUpdatedListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080085
Enrico Ros1f5a0952014-11-18 20:15:48 -080086 private Observable mTabCountObservable;
87
The Android Open Source Project0c908882009-03-03 19:32:16 -080088 /**
Michael Kolb8233fac2010-10-26 16:08:53 -070089 * Construct a new TabControl object
The Android Open Source Project0c908882009-03-03 19:32:16 -080090 */
Michael Kolb8233fac2010-10-26 16:08:53 -070091 TabControl(Controller controller) {
92 mController = controller;
Michael Kolb8233fac2010-10-26 16:08:53 -070093 mMaxTabs = mController.getMaxTabs();
Michael Kolb6e4653e2010-09-27 16:22:38 -070094 mTabs = new ArrayList<Tab>(mMaxTabs);
95 mTabQueue = new ArrayList<Tab>(mMaxTabs);
Enrico Ros1f5a0952014-11-18 20:15:48 -080096 mTabCountObservable = new Observable();
97 mTabCountObservable.set(0);
The Android Open Source Project0c908882009-03-03 19:32:16 -080098 }
99
John Reck52be4782011-08-26 15:37:29 -0700100 synchronized static long getNextId() {
Michael Kolbc831b632011-05-11 09:30:34 -0700101 return sNextId++;
102 }
103
Enrico Ros1f5a0952014-11-18 20:15:48 -0800104 Observable getTabCountObservable() {
105 return mTabCountObservable;
106 }
107
Michael Kolb68775752010-08-19 12:42:01 -0700108 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800109 * Return the current tab's main WebView. This will always return the main
110 * WebView for a given tab and not a subwindow.
111 * @return The current tab's WebView.
112 */
113 WebView getCurrentWebView() {
114 Tab t = getTab(mCurrentTab);
115 if (t == null) {
116 return null;
117 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700118 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +0100119 }
120
121 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800122 * Return the current tab's top-level WebView. This can return a subwindow
123 * if one exists.
124 * @return The top-level WebView of the current tab.
125 */
126 WebView getCurrentTopWebView() {
127 Tab t = getTab(mCurrentTab);
128 if (t == null) {
129 return null;
130 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700131 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800132 }
133
134 /**
135 * Return the current tab's subwindow if it exists.
136 * @return The subwindow of the current tab or null if it doesn't exist.
137 */
138 WebView getCurrentSubWindow() {
139 Tab t = getTab(mCurrentTab);
140 if (t == null) {
141 return null;
142 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700143 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800144 }
145
146 /**
Michael Kolb1bf23132010-11-19 12:55:12 -0800147 * return the list of tabs
148 */
149 List<Tab> getTabs() {
150 return mTabs;
151 }
152
153 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700154 * Return the tab at the specified position.
155 * @return The Tab for the specified position or null if the tab does not
The Android Open Source Project0c908882009-03-03 19:32:16 -0800156 * exist.
157 */
Michael Kolbc831b632011-05-11 09:30:34 -0700158 Tab getTab(int position) {
159 if (position >= 0 && position < mTabs.size()) {
160 return mTabs.get(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800161 }
162 return null;
163 }
164
165 /**
166 * Return the current tab.
167 * @return The current tab.
168 */
169 Tab getCurrentTab() {
170 return getTab(mCurrentTab);
171 }
172
173 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700174 * Return the current tab position.
175 * @return The current tab position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800176 */
Michael Kolbc831b632011-05-11 09:30:34 -0700177 int getCurrentPosition() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800178 return mCurrentTab;
179 }
Michael Kolbfe251992010-07-08 15:41:55 -0700180
The Android Open Source Project0c908882009-03-03 19:32:16 -0800181 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700182 * Given a Tab, find it's position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800183 * @param Tab to find
Michael Kolbc831b632011-05-11 09:30:34 -0700184 * @return position of Tab or -1 if not found
The Android Open Source Project0c908882009-03-03 19:32:16 -0800185 */
Michael Kolbc831b632011-05-11 09:30:34 -0700186 int getTabPosition(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400187 if (tab == null) {
188 return -1;
189 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800190 return mTabs.indexOf(tab);
191 }
192
Grace Kloba22ac16e2009-10-07 18:00:23 -0700193 boolean canCreateNewTab() {
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100194 return mMaxTabs > mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700195 }
196
The Android Open Source Project0c908882009-03-03 19:32:16 -0800197 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700198 * Returns true if there are any incognito tabs open.
199 * @return True when any incognito tabs are open, false otherwise.
200 */
201 boolean hasAnyOpenIncognitoTabs() {
202 for (Tab tab : mTabs) {
Michael Kolbc831b632011-05-11 09:30:34 -0700203 if (tab.getWebView() != null
204 && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700205 return true;
206 }
207 }
208 return false;
209 }
210
Michael Kolb14612442011-06-24 13:06:29 -0700211 void addPreloadedTab(Tab tab) {
Mathew Inwoode09305e2011-09-02 12:03:26 +0100212 for (Tab current : mTabs) {
213 if (current != null && current.getId() == tab.getId()) {
214 throw new IllegalStateException("Tab with id " + tab.getId() + " already exists: "
215 + current.toString());
216 }
217 }
Michael Kolb14612442011-06-24 13:06:29 -0700218 mTabs.add(tab);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800219 mTabCountObservable.set(mTabs.size());
Michael Kolb14612442011-06-24 13:06:29 -0700220 tab.setController(mController);
221 mController.onSetWebView(tab, tab.getWebView());
222 tab.putInBackground();
223 }
224
Elliott Slaughtere440a882010-08-20 13:54:45 -0700225 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700226 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800227 * @return The newly createTab or null if we have reached the maximum
228 * number of open tabs.
229 */
Michael Kolb7bcafde2011-05-09 13:55:59 -0700230 Tab createNewTab(boolean privateBrowsing) {
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500231 return createNewTab(null, privateBrowsing, false);
232 }
233
234 Tab createNewTab(boolean privateBrowsing, boolean backgroundTab) {
235 return createNewTab(null, privateBrowsing, backgroundTab);
John Reck1cf4b792011-07-26 10:22:22 -0700236 }
237
238 Tab createNewTab(Bundle state, boolean privateBrowsing) {
Stewart Chao3f628f72015-01-07 10:03:18 -0500239 return createNewTab(state, privateBrowsing, false);
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500240 }
241
242 Tab createNewTab(Bundle state, boolean privateBrowsing, boolean backgroundTab) {
John Reck1cf4b792011-07-26 10:22:22 -0700243 int size = mTabs.size();
244 // Return false if we have maxed out on tabs
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100245 if (!canCreateNewTab()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800246 return null;
247 }
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100248
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500249 final WebView w = createNewWebView(privateBrowsing, backgroundTab);
Steve Block2bc69912009-07-30 14:45:13 +0100250
The Android Open Source Project0c908882009-03-03 19:32:16 -0800251 // Create a new tab and add it to the tab list
Vivek Sekhar0c609cd2015-12-10 16:00:05 -0800252 Tab t = new Tab(mController, w, state, backgroundTab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800253 mTabs.add(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800254 mTabCountObservable.set(mTabs.size());
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700255 if (privateBrowsing) {
256 mNumIncognito += 1;
257 }
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700258 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700259 t.putInBackground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800260 return t;
261 }
262
263 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700264 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700265 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700266 */
267 Tab createNewTab() {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700268 return createNewTab(false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700269 }
270
Axesh R. Ajmera4b000312015-07-23 10:00:16 -0700271 SnapshotTab createSnapshotTab(long snapshotId, Bundle state) {
272 SnapshotTab t = new SnapshotTab(mController, snapshotId, state);
John Reckd8c74522011-06-14 08:45:00 -0700273 mTabs.add(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800274 mTabCountObservable.set(mTabs.size());
John Reckd8c74522011-06-14 08:45:00 -0700275 return t;
276 }
277
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700278 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500279 * Remove the parent child relationships from all tabs.
280 */
281 void removeParentChildRelationShips() {
282 for (Tab tab : mTabs) {
283 tab.removeFromTree();
284 }
285 }
286
287 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800288 * Remove the tab from the list. If the tab is the current tab shown, the
289 * last created tab will be shown.
290 * @param t The tab to be removed.
291 */
292 boolean removeTab(Tab t) {
293 if (t == null) {
294 return false;
295 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700296
Patrick Scottd944d4d2010-01-27 16:39:11 -0500297 // Grab the current tab before modifying the list.
298 Tab current = getCurrentTab();
299
300 // Remove t from our list of tabs.
301 mTabs.remove(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800302 mTabCountObservable.set(mTabs.size());
Patrick Scottd944d4d2010-01-27 16:39:11 -0500303
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700304 //Clear incognito geolocation state if this is the last incognito tab.
305 if (t.isPrivateBrowsingEnabled()) {
306 mNumIncognito -= 1;
307 if (mNumIncognito == 0) {
308 GeolocationPermissions.onIncognitoTabsRemoved();
309 }
310 }
311
Patrick Scottd944d4d2010-01-27 16:39:11 -0500312 // Put the tab in the background only if it is the current one.
313 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700314 t.putInBackground();
315 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500316 } else {
317 // If a tab that is earlier in the list gets removed, the current
318 // index no longer points to the correct tab.
Michael Kolbc831b632011-05-11 09:30:34 -0700319 mCurrentTab = getTabPosition(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800320 }
321
Grace Kloba22ac16e2009-10-07 18:00:23 -0700322 // destroy the tab
323 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800324 // clear it's references to parent and children
325 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800326
The Android Open Source Project0c908882009-03-03 19:32:16 -0800327 // Remove it from the queue of viewed tabs.
328 mTabQueue.remove(t);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800329 return true;
330 }
331
332 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800333 * Destroy all the tabs and subwindows
334 */
335 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800336 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700337 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800338 }
339 mTabs.clear();
340 mTabQueue.clear();
341 }
342
343 /**
344 * Returns the number of tabs created.
345 * @return The number of tabs created.
346 */
347 int getTabCount() {
348 return mTabs.size();
349 }
350
The Android Open Source Project0c908882009-03-03 19:32:16 -0800351 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700352 * save the tab state:
353 * current position
354 * position sorted array of tab ids
355 * for each tab id, save the tab state
356 * @param outState
John Reckaed9c542011-05-27 16:08:53 -0700357 * @param saveImages
The Android Open Source Project0c908882009-03-03 19:32:16 -0800358 */
John Reck1cf4b792011-07-26 10:22:22 -0700359 void saveState(Bundle outState) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800360 final int numTabs = getTabCount();
John Reck24f18262011-06-17 14:47:20 -0700361 if (numTabs == 0) {
362 return;
363 }
Michael Kolbc831b632011-05-11 09:30:34 -0700364 long[] ids = new long[numTabs];
365 int i = 0;
366 for (Tab tab : mTabs) {
John Reck1cf4b792011-07-26 10:22:22 -0700367 Bundle tabState = tab.saveState();
Tarun Nainani8eb00912014-07-17 12:28:32 -0700368 if (tabState != null && tab.isPrivateBrowsingEnabled() == false) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700369 ids[i++] = tab.getId();
John Reck52be4782011-08-26 15:37:29 -0700370 String key = Long.toString(tab.getId());
371 if (outState.containsKey(key)) {
372 // Dump the tab state for debugging purposes
373 for (Tab dt : mTabs) {
374 Log.e(LOGTAG, dt.toString());
375 }
376 throw new IllegalStateException(
377 "Error saving state, duplicate tab ids!");
378 }
379 outState.putBundle(key, tabState);
Michael Kolb3ae7f742011-07-13 15:18:25 -0700380 } else {
381 ids[i++] = -1;
John Reck52be4782011-08-26 15:37:29 -0700382 // Since we won't be restoring the thumbnail, delete it
383 tab.deleteThumbnail();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800384 }
385 }
John Reck24f18262011-06-17 14:47:20 -0700386 if (!outState.isEmpty()) {
387 outState.putLongArray(POSITIONS, ids);
388 Tab current = getCurrentTab();
389 long cid = -1;
390 if (current != null) {
391 cid = current.getId();
392 }
393 outState.putLong(CURRENT, cid);
Michael Kolbdbf39812011-06-10 14:06:40 -0700394 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800395 }
396
397 /**
Patrick Scott7d50a932011-02-04 09:27:26 -0500398 * Check if the state can be restored. If the state can be restored, the
Michael Kolbc831b632011-05-11 09:30:34 -0700399 * current tab id is returned. This can be passed to restoreState below
Patrick Scott7d50a932011-02-04 09:27:26 -0500400 * in order to restore the correct tab. Otherwise, -1 is returned and the
401 * state cannot be restored.
402 */
Michael Kolbc831b632011-05-11 09:30:34 -0700403 long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
404 final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
405 if (ids == null) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500406 return -1;
407 }
Michael Kolbc831b632011-05-11 09:30:34 -0700408 final long oldcurrent = inState.getLong(CURRENT);
409 long current = -1;
Michael Kolb3ae7f742011-07-13 15:18:25 -0700410 if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
John Reck1cf4b792011-07-26 10:22:22 -0700411 current = oldcurrent;
Patrick Scott7d50a932011-02-04 09:27:26 -0500412 } else {
Michael Kolbc831b632011-05-11 09:30:34 -0700413 // pick first non incognito tab
414 for (long id : ids) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700415 if (hasState(id, inState) && !isIncognito(id, inState)) {
Michael Kolbc831b632011-05-11 09:30:34 -0700416 current = id;
Patrick Scott7d50a932011-02-04 09:27:26 -0500417 break;
418 }
419 }
420 }
Michael Kolbc831b632011-05-11 09:30:34 -0700421 return current;
Patrick Scott7d50a932011-02-04 09:27:26 -0500422 }
423
Michael Kolb3ae7f742011-07-13 15:18:25 -0700424 private boolean hasState(long id, Bundle state) {
425 if (id == -1) return false;
426 Bundle tab = state.getBundle(Long.toString(id));
427 return ((tab != null) && !tab.isEmpty());
428 }
429
430 private boolean isIncognito(long id, Bundle state) {
431 Bundle tabstate = state.getBundle(Long.toString(id));
432 if ((tabstate != null) && !tabstate.isEmpty()) {
433 return tabstate.getBoolean(Tab.INCOGNITO);
434 }
435 return false;
436 }
437
Patrick Scott7d50a932011-02-04 09:27:26 -0500438 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800439 * Restore the state of all the tabs.
Michael Kolbc831b632011-05-11 09:30:34 -0700440 * @param currentId The tab id to restore.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800441 * @param inState The saved state of all the tabs.
Michael Kolb1bf23132010-11-19 12:55:12 -0800442 * @param restoreIncognitoTabs Restoring private browsing tabs
443 * @param restoreAll All webviews get restored, not just the current tab
444 * (this does not override handling of incognito tabs)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800445 */
Michael Kolbc831b632011-05-11 09:30:34 -0700446 void restoreState(Bundle inState, long currentId,
Patrick Scott7d50a932011-02-04 09:27:26 -0500447 boolean restoreIncognitoTabs, boolean restoreAll) {
Michael Kolbc831b632011-05-11 09:30:34 -0700448 if (currentId == -1) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500449 return;
450 }
Michael Kolbc831b632011-05-11 09:30:34 -0700451 long[] ids = inState.getLongArray(POSITIONS);
452 long maxId = -Long.MAX_VALUE;
453 HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>();
454 for (long id : ids) {
455 if (id > maxId) {
456 maxId = id;
457 }
458 final String idkey = Long.toString(id);
459 Bundle state = inState.getBundle(idkey);
John Reckd8c74522011-06-14 08:45:00 -0700460 if (state == null || state.isEmpty()) {
461 // Skip tab
462 continue;
463 } else if (!restoreIncognitoTabs
Michael Kolbc831b632011-05-11 09:30:34 -0700464 && state.getBoolean(Tab.INCOGNITO)) {
465 // ignore tab
466 } else if (id == currentId || restoreAll) {
Axesh R. Ajmerae6b11aa2015-03-16 18:20:41 -0700467 Tab t = null;
468 // Add special check to restore Snapshot Tab if needed
469 if (state.getLong(SnapshotTab.SNAPSHOT_ID, -1) != -1 ) {
Axesh R. Ajmera4b000312015-07-23 10:00:16 -0700470 t = (SnapshotTab) createSnapshotTab( state.getLong(SnapshotTab.SNAPSHOT_ID), state);
Axesh R. Ajmerae6b11aa2015-03-16 18:20:41 -0700471 } else {
472 // presume its a normal Tab
473 t = createNewTab(state, false);
474 }
475
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100476 if (t == null) {
477 // We could "break" at this point, but we want
478 // sNextId to be set correctly.
479 continue;
480 }
Michael Kolbc831b632011-05-11 09:30:34 -0700481 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500482 // Me must set the current tab before restoring the state
483 // so that all the client classes are set.
Michael Kolbc831b632011-05-11 09:30:34 -0700484 if (id == currentId) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500485 setCurrentTab(t);
486 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700487 } else {
Patrick Scott7d50a932011-02-04 09:27:26 -0500488 // Create a new tab and don't restore the state yet, add it
489 // to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700490 Tab t = new Tab(mController, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700491 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500492 mTabs.add(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800493 mTabCountObservable.set(mTabs.size());
Patrick Scott7d50a932011-02-04 09:27:26 -0500494 // added the tab to the front as they are not current
495 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700496 }
Michael Kolbc831b632011-05-11 09:30:34 -0700497 }
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100498
499 // make sure that there is no id overlap between the restored
500 // and new tabs
501 sNextId = maxId + 1;
502
John Reckd8c74522011-06-14 08:45:00 -0700503 if (mCurrentTab == -1) {
504 if (getTabCount() > 0) {
505 setCurrentTab(getTab(0));
John Reckd8c74522011-06-14 08:45:00 -0700506 }
507 }
Michael Kolbc831b632011-05-11 09:30:34 -0700508 // restore parent/child relationships
509 for (long id : ids) {
510 final Tab tab = tabMap.get(id);
511 final Bundle b = inState.getBundle(Long.toString(id));
512 if ((b != null) && (tab != null)) {
513 final long parentId = b.getLong(Tab.PARENTTAB, -1);
514 if (parentId != -1) {
515 final Tab parent = tabMap.get(parentId);
Patrick Scott7d50a932011-02-04 09:27:26 -0500516 if (parent != null) {
Michael Kolbc831b632011-05-11 09:30:34 -0700517 parent.addChildTab(tab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800518 }
519 }
520 }
521 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800522 }
523
524 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700525 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800526 * WebView cache;
527 */
528 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700529 if (getTabCount() == 0) return;
530
Grace Klobaf56f68d2010-04-11 22:53:42 -0700531 // free the least frequently used background tabs
532 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
533 if (tabs.size() > 0) {
534 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
535 for (Tab t : tabs) {
536 // store the WebView's state.
537 t.saveState();
538 // destroy the tab
539 t.destroy();
540 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800541 return;
542 }
543
Derek Sollenberger4433d032009-06-10 15:37:21 -0400544 // free the WebView's unused memory (this includes the cache)
545 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800546 WebView view = getCurrentWebView();
547 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400548 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800549 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800550 }
551
Grace Klobaf56f68d2010-04-11 22:53:42 -0700552 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
553 Vector<Tab> tabsToGo = new Vector<Tab>();
554
Patrick Scott2a67de42009-08-31 09:48:37 -0400555 // Don't do anything if we only have 1 tab or if the current tab is
556 // null.
557 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700558 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800559 }
560
Grace Klobaf56f68d2010-04-11 22:53:42 -0700561 if (mTabQueue.size() == 0) {
562 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800563 }
564
Grace Klobaf56f68d2010-04-11 22:53:42 -0700565 // Rip through the queue starting at the beginning and tear down half of
566 // available tabs which are not the current tab or the parent of the
567 // current tab.
568 int openTabCount = 0;
569 for (Tab t : mTabQueue) {
570 if (t != null && t.getWebView() != null) {
571 openTabCount++;
Michael Kolbc831b632011-05-11 09:30:34 -0700572 if (t != current && t != current.getParent()) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700573 tabsToGo.add(t);
574 }
575 }
576 }
577
578 openTabCount /= 2;
579 if (tabsToGo.size() > openTabCount) {
580 tabsToGo.setSize(openTabCount);
581 }
582
583 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800584 }
585
Michael Kolbff6a7482011-07-26 16:37:15 -0700586 Tab getLeastUsedTab(Tab current) {
587 if (getTabCount() == 1 || current == null) {
588 return null;
589 }
590 if (mTabQueue.size() == 0) {
591 return null;
592 }
593 // find a tab which is not the current tab or the parent of the
594 // current tab
595 for (Tab t : mTabQueue) {
596 if (t != null && t.getWebView() != null) {
597 if (t != current && t != current.getParent()) {
598 return t;
599 }
600 }
601 }
602 return null;
603 }
604
The Android Open Source Project0c908882009-03-03 19:32:16 -0800605 /**
606 * Show the tab that contains the given WebView.
607 * @param view The WebView used to find the tab.
608 */
609 Tab getTabFromView(WebView view) {
Michael Kolbc831b632011-05-11 09:30:34 -0700610 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700611 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800612 return t;
613 }
614 }
615 return null;
616 }
617
618 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700619 * Return the tab with the matching application id.
620 * @param id The application identifier.
621 */
Michael Kolbc831b632011-05-11 09:30:34 -0700622 Tab getTabFromAppId(String id) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700623 if (id == null) {
624 return null;
625 }
Michael Kolbc831b632011-05-11 09:30:34 -0700626 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700627 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700628 return t;
629 }
630 }
631 return null;
632 }
633
Grace Kloba22ac16e2009-10-07 18:00:23 -0700634 /**
635 * Stop loading in all opened WebView including subWindows.
636 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700637 void stopAllLoading() {
Michael Kolbc831b632011-05-11 09:30:34 -0700638 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700639 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700640 if (webview != null) {
641 webview.stopLoading();
642 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700643 final WebView subview = t.getSubWebView();
644 if (subview != null) {
Mattias Nilsson9727af82012-06-14 17:07:56 +0200645 subview.stopLoading();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700646 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700647 }
648 }
649
John Reckdb22ec42011-06-29 11:31:24 -0700650 // This method checks if a tab matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400651 private boolean tabMatchesUrl(Tab t, String url) {
John Reckdb22ec42011-06-29 11:31:24 -0700652 return url.equals(t.getUrl()) || url.equals(t.getOriginalUrl());
Patrick Scottcd115892009-07-16 09:42:58 -0400653 }
654
655 /**
John Reckdb22ec42011-06-29 11:31:24 -0700656 * Return the tab that matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400657 * @param url The url to search for.
658 */
John Reckdb22ec42011-06-29 11:31:24 -0700659 Tab findTabWithUrl(String url) {
Patrick Scottcd115892009-07-16 09:42:58 -0400660 if (url == null) {
661 return null;
662 }
663 // Check the current tab first.
John Reckdb22ec42011-06-29 11:31:24 -0700664 Tab currentTab = getCurrentTab();
665 if (currentTab != null && tabMatchesUrl(currentTab, url)) {
666 return currentTab;
Patrick Scottcd115892009-07-16 09:42:58 -0400667 }
668 // Now check all the rest.
Michael Kolbc831b632011-05-11 09:30:34 -0700669 for (Tab tab : mTabs) {
670 if (tabMatchesUrl(tab, url)) {
John Reckdb22ec42011-06-29 11:31:24 -0700671 return tab;
Patrick Scottcd115892009-07-16 09:42:58 -0400672 }
673 }
674 return null;
675 }
676
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700677 /**
John Reck30c714c2010-12-16 17:30:34 -0800678 * Recreate the main WebView of the given tab.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700679 */
John Reck30c714c2010-12-16 17:30:34 -0800680 void recreateWebView(Tab t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700681 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700682 if (w != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700683 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700684 }
685 // Create a new WebView. If this tab is the current tab, we need to put
686 // back all the clients so force it to be the current tab.
Panos Thomasa9a5a582014-03-18 19:20:08 -0700687 t.setWebView(createNewWebView(t.isPrivateBrowsingEnabled()), false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700688 if (getCurrentTab() == t) {
689 setCurrentTab(t, true);
690 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700691 }
692
693 /**
694 * Creates a new WebView and registers it with the global settings.
695 */
696 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700697 return createNewWebView(false);
698 }
699
700 /**
701 * Creates a new WebView and registers it with the global settings.
702 * @param privateBrowsing When true, enables private browsing in the new
703 * WebView.
704 */
705 private WebView createNewWebView(boolean privateBrowsing) {
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500706 return createNewWebView(privateBrowsing, false);
707 }
708
709 private WebView createNewWebView(boolean privateBrowsing, boolean backgroundTab) {
710 return mController.getWebViewFactory().createWebView(privateBrowsing, backgroundTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700711 }
712
713 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800714 * Put the current tab in the background and set newTab as the current tab.
715 * @param newTab The new tab. If newTab is null, the current tab is not
716 * set.
717 */
718 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700719 return setCurrentTab(newTab, false);
720 }
721
722 /**
723 * If force is true, this method skips the check for newTab == current.
724 */
725 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800726 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700727 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800728 return true;
729 }
730 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700731 current.putInBackground();
732 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800733 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800734 if (newTab == null) {
735 return false;
736 }
737
738 // Move the newTab to the end of the queue
739 int index = mTabQueue.indexOf(newTab);
740 if (index != -1) {
741 mTabQueue.remove(index);
742 }
743 mTabQueue.add(newTab);
744
The Android Open Source Project0c908882009-03-03 19:32:16 -0800745 // Display the new current tab
746 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700747 WebView mainView = newTab.getWebView();
John Reckd8c74522011-06-14 08:45:00 -0700748 boolean needRestore = !newTab.isSnapshot() && (mainView == null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800749 if (needRestore) {
750 // Same work as in createNewTab() except don't do new Tab()
Panos Thomasa9a5a582014-03-18 19:20:08 -0700751 mainView = createNewWebView(newTab.isPrivateBrowsingEnabled());
Steve Block2bc69912009-07-30 14:45:13 +0100752 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800753 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700754 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800755 return true;
756 }
Michael Kolbfe251992010-07-08 15:41:55 -0700757
John Reck8ee633f2011-08-09 16:00:35 -0700758 public void setOnThumbnailUpdatedListener(OnThumbnailUpdatedListener listener) {
759 mOnThumbnailUpdatedListener = listener;
760 for (Tab t : mTabs) {
761 WebView web = t.getWebView();
762 if (web != null) {
763 web.setPictureListener(listener != null ? t : null);
764 }
765 }
766 }
767
768 public OnThumbnailUpdatedListener getOnThumbnailUpdatedListener() {
769 return mOnThumbnailUpdatedListener;
770 }
771
The Android Open Source Project0c908882009-03-03 19:32:16 -0800772}