blob: 00f3c44e8195d2e6fdde68020285043d1100c1ce [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;
Vivek Sekhar2f2ff572015-12-21 11:55:17 -080025import org.codeaurora.swe.Engine;
Enrico Ros1f5a0952014-11-18 20:15:48 -080026import org.codeaurora.swe.util.Observable;
The Android Open Source Project0c908882009-03-03 19:32:16 -080027
The Android Open Source Project0c908882009-03-03 19:32:16 -080028import java.util.ArrayList;
Elliott Slaughter3d6df162010-08-25 13:17:44 -070029import java.util.HashMap;
Michael Kolb1bf23132010-11-19 12:55:12 -080030import java.util.List;
The Android Open Source Project0c908882009-03-03 19:32:16 -080031import java.util.Vector;
32
33class TabControl {
34 // Log Tag
35 private static final String LOGTAG = "TabControl";
Michael Kolbc831b632011-05-11 09:30:34 -070036
John Reckd8c74522011-06-14 08:45:00 -070037 // next Tab ID, starting at 1
38 private static long sNextId = 1;
Michael Kolbc831b632011-05-11 09:30:34 -070039
40 private static final String POSITIONS = "positions";
41 private static final String CURRENT = "current";
42
Pankaj Garg75273bb2015-08-20 11:43:49 -070043
44 /*
45 Find and reload any live tabs that have loaded the given URL.
46 Note - Upto 2 tabs are live at any given moment.
47 */
48 public void findAndReload(String origin) {
49 for (Tab tab : mTabs){
50 if (tab.getWebView() != null) {
51 Uri url = Uri.parse(tab.getWebView().getUrl());
52 if (url.getHost().equals(origin)){
53 tab.getWebView().reload();
54 }
55 }
56 }
57 }
58
59 // Reload the all the live tabs
60 public void reloadLiveTabs() {
61 for (Tab tab : mTabs) {
62 if (tab.getWebView() != null) {
63 tab.getWebView().reload();
64 }
65 }
66 }
67
John Reck8ee633f2011-08-09 16:00:35 -070068 public static interface OnThumbnailUpdatedListener {
69 void onThumbnailUpdated(Tab t);
70 }
71
The Android Open Source Project0c908882009-03-03 19:32:16 -080072 // Maximum number of tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070073 private int mMaxTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080074 // Private array of WebViews that are used as tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070075 private ArrayList<Tab> mTabs;
The Android Open Source Project0c908882009-03-03 19:32:16 -080076 // Queue of most recently viewed tabs.
Michael Kolb6e4653e2010-09-27 16:22:38 -070077 private ArrayList<Tab> mTabQueue;
The Android Open Source Project0c908882009-03-03 19:32:16 -080078 // Current position in mTabs.
79 private int mCurrentTab = -1;
Michael Kolb8233fac2010-10-26 16:08:53 -070080 // the main browser controller
81 private final Controller mController;
Axesh R. Ajmera2e241242014-05-19 15:53:38 -070082 // number of incognito tabs
83 private int mNumIncognito = 0;
Michael Kolb8233fac2010-10-26 16:08:53 -070084
John Reck8ee633f2011-08-09 16:00:35 -070085 private OnThumbnailUpdatedListener mOnThumbnailUpdatedListener;
The Android Open Source Project0c908882009-03-03 19:32:16 -080086
Enrico Ros1f5a0952014-11-18 20:15:48 -080087 private Observable mTabCountObservable;
88
The Android Open Source Project0c908882009-03-03 19:32:16 -080089 /**
Michael Kolb8233fac2010-10-26 16:08:53 -070090 * Construct a new TabControl object
The Android Open Source Project0c908882009-03-03 19:32:16 -080091 */
Michael Kolb8233fac2010-10-26 16:08:53 -070092 TabControl(Controller controller) {
93 mController = controller;
Michael Kolb8233fac2010-10-26 16:08:53 -070094 mMaxTabs = mController.getMaxTabs();
Michael Kolb6e4653e2010-09-27 16:22:38 -070095 mTabs = new ArrayList<Tab>(mMaxTabs);
96 mTabQueue = new ArrayList<Tab>(mMaxTabs);
Enrico Ros1f5a0952014-11-18 20:15:48 -080097 mTabCountObservable = new Observable();
98 mTabCountObservable.set(0);
The Android Open Source Project0c908882009-03-03 19:32:16 -080099 }
100
John Reck52be4782011-08-26 15:37:29 -0700101 synchronized static long getNextId() {
Michael Kolbc831b632011-05-11 09:30:34 -0700102 return sNextId++;
103 }
104
Enrico Ros1f5a0952014-11-18 20:15:48 -0800105 Observable getTabCountObservable() {
106 return mTabCountObservable;
107 }
108
Michael Kolb68775752010-08-19 12:42:01 -0700109 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800110 * Return the current tab's main WebView. This will always return the main
111 * WebView for a given tab and not a subwindow.
112 * @return The current tab's WebView.
113 */
114 WebView getCurrentWebView() {
115 Tab t = getTab(mCurrentTab);
116 if (t == null) {
117 return null;
118 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700119 return t.getWebView();
Ben Murdochbff2d602009-07-01 20:19:05 +0100120 }
121
122 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800123 * Return the current tab's top-level WebView. This can return a subwindow
124 * if one exists.
125 * @return The top-level WebView of the current tab.
126 */
127 WebView getCurrentTopWebView() {
128 Tab t = getTab(mCurrentTab);
129 if (t == null) {
130 return null;
131 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700132 return t.getTopWindow();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800133 }
134
135 /**
136 * Return the current tab's subwindow if it exists.
137 * @return The subwindow of the current tab or null if it doesn't exist.
138 */
139 WebView getCurrentSubWindow() {
140 Tab t = getTab(mCurrentTab);
141 if (t == null) {
142 return null;
143 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700144 return t.getSubWebView();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800145 }
146
147 /**
Michael Kolb1bf23132010-11-19 12:55:12 -0800148 * return the list of tabs
149 */
150 List<Tab> getTabs() {
151 return mTabs;
152 }
153
154 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700155 * Return the tab at the specified position.
156 * @return The Tab for the specified position or null if the tab does not
The Android Open Source Project0c908882009-03-03 19:32:16 -0800157 * exist.
158 */
Michael Kolbc831b632011-05-11 09:30:34 -0700159 Tab getTab(int position) {
160 if (position >= 0 && position < mTabs.size()) {
161 return mTabs.get(position);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800162 }
163 return null;
164 }
165
166 /**
167 * Return the current tab.
168 * @return The current tab.
169 */
170 Tab getCurrentTab() {
171 return getTab(mCurrentTab);
172 }
173
174 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700175 * Return the current tab position.
176 * @return The current tab position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800177 */
Michael Kolbc831b632011-05-11 09:30:34 -0700178 int getCurrentPosition() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800179 return mCurrentTab;
180 }
Michael Kolbfe251992010-07-08 15:41:55 -0700181
The Android Open Source Project0c908882009-03-03 19:32:16 -0800182 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700183 * Given a Tab, find it's position
The Android Open Source Project0c908882009-03-03 19:32:16 -0800184 * @param Tab to find
Michael Kolbc831b632011-05-11 09:30:34 -0700185 * @return position of Tab or -1 if not found
The Android Open Source Project0c908882009-03-03 19:32:16 -0800186 */
Michael Kolbc831b632011-05-11 09:30:34 -0700187 int getTabPosition(Tab tab) {
Patrick Scottae641ac2009-04-20 13:51:49 -0400188 if (tab == null) {
189 return -1;
190 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800191 return mTabs.indexOf(tab);
192 }
193
Grace Kloba22ac16e2009-10-07 18:00:23 -0700194 boolean canCreateNewTab() {
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100195 return mMaxTabs > mTabs.size();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700196 }
197
The Android Open Source Project0c908882009-03-03 19:32:16 -0800198 /**
Elliott Slaughtere440a882010-08-20 13:54:45 -0700199 * Returns true if there are any incognito tabs open.
200 * @return True when any incognito tabs are open, false otherwise.
201 */
202 boolean hasAnyOpenIncognitoTabs() {
203 for (Tab tab : mTabs) {
Michael Kolbc831b632011-05-11 09:30:34 -0700204 if (tab.getWebView() != null
205 && tab.getWebView().isPrivateBrowsingEnabled()) {
Elliott Slaughtere440a882010-08-20 13:54:45 -0700206 return true;
207 }
208 }
209 return false;
210 }
211
Michael Kolb14612442011-06-24 13:06:29 -0700212 void addPreloadedTab(Tab tab) {
Mathew Inwoode09305e2011-09-02 12:03:26 +0100213 for (Tab current : mTabs) {
214 if (current != null && current.getId() == tab.getId()) {
215 throw new IllegalStateException("Tab with id " + tab.getId() + " already exists: "
216 + current.toString());
217 }
218 }
Michael Kolb14612442011-06-24 13:06:29 -0700219 mTabs.add(tab);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800220 mTabCountObservable.set(mTabs.size());
Michael Kolb14612442011-06-24 13:06:29 -0700221 tab.setController(mController);
222 mController.onSetWebView(tab, tab.getWebView());
223 tab.putInBackground();
224 }
225
Elliott Slaughtere440a882010-08-20 13:54:45 -0700226 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700227 * Create a new tab.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800228 * @return The newly createTab or null if we have reached the maximum
229 * number of open tabs.
230 */
Michael Kolb7bcafde2011-05-09 13:55:59 -0700231 Tab createNewTab(boolean privateBrowsing) {
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500232 return createNewTab(null, privateBrowsing, false);
233 }
234
235 Tab createNewTab(boolean privateBrowsing, boolean backgroundTab) {
236 return createNewTab(null, privateBrowsing, backgroundTab);
John Reck1cf4b792011-07-26 10:22:22 -0700237 }
238
239 Tab createNewTab(Bundle state, boolean privateBrowsing) {
Stewart Chao3f628f72015-01-07 10:03:18 -0500240 return createNewTab(state, privateBrowsing, false);
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500241 }
242
243 Tab createNewTab(Bundle state, boolean privateBrowsing, boolean backgroundTab) {
John Reck1cf4b792011-07-26 10:22:22 -0700244 int size = mTabs.size();
245 // Return false if we have maxed out on tabs
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100246 if (!canCreateNewTab()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800247 return null;
248 }
Narayan Kamathc3ad9ea2011-07-26 15:20:21 +0100249
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500250 final WebView w = createNewWebView(privateBrowsing, backgroundTab);
Steve Block2bc69912009-07-30 14:45:13 +0100251
The Android Open Source Project0c908882009-03-03 19:32:16 -0800252 // Create a new tab and add it to the tab list
Vivek Sekhar0c609cd2015-12-10 16:00:05 -0800253 Tab t = new Tab(mController, w, state, backgroundTab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800254 mTabs.add(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800255 mTabCountObservable.set(mTabs.size());
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700256 if (privateBrowsing) {
257 mNumIncognito += 1;
258 }
The Android Open Source Project4e5f5872009-03-09 11:52:14 -0700259 // Initially put the tab in the background.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700260 t.putInBackground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800261 return t;
262 }
263
264 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700265 * Create a new tab with default values for closeOnExit(false),
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700266 * appId(null), url(null), and privateBrowsing(false).
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700267 */
268 Tab createNewTab() {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700269 return createNewTab(false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700270 }
271
Axesh R. Ajmera4b000312015-07-23 10:00:16 -0700272 SnapshotTab createSnapshotTab(long snapshotId, Bundle state) {
273 SnapshotTab t = new SnapshotTab(mController, snapshotId, state);
John Reckd8c74522011-06-14 08:45:00 -0700274 mTabs.add(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800275 mTabCountObservable.set(mTabs.size());
John Reckd8c74522011-06-14 08:45:00 -0700276 return t;
277 }
278
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700279 /**
Leon Scrogginsfde97462010-01-11 13:06:21 -0500280 * Remove the parent child relationships from all tabs.
281 */
282 void removeParentChildRelationShips() {
283 for (Tab tab : mTabs) {
284 tab.removeFromTree();
285 }
286 }
287
288 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800289 * Remove the tab from the list. If the tab is the current tab shown, the
290 * last created tab will be shown.
291 * @param t The tab to be removed.
292 */
293 boolean removeTab(Tab t) {
294 if (t == null) {
295 return false;
296 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700297
Patrick Scottd944d4d2010-01-27 16:39:11 -0500298 // Grab the current tab before modifying the list.
299 Tab current = getCurrentTab();
300
301 // Remove t from our list of tabs.
302 mTabs.remove(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800303 mTabCountObservable.set(mTabs.size());
Patrick Scottd944d4d2010-01-27 16:39:11 -0500304
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700305 //Clear incognito geolocation state if this is the last incognito tab.
306 if (t.isPrivateBrowsingEnabled()) {
307 mNumIncognito -= 1;
308 if (mNumIncognito == 0) {
309 GeolocationPermissions.onIncognitoTabsRemoved();
Vivek Sekhar2f2ff572015-12-21 11:55:17 -0800310 Engine.destroyIncognitoProfile();
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700311 }
312 }
313
Patrick Scottd944d4d2010-01-27 16:39:11 -0500314 // Put the tab in the background only if it is the current one.
315 if (current == t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700316 t.putInBackground();
317 mCurrentTab = -1;
Patrick Scottd944d4d2010-01-27 16:39:11 -0500318 } else {
319 // If a tab that is earlier in the list gets removed, the current
320 // index no longer points to the correct tab.
Michael Kolbc831b632011-05-11 09:30:34 -0700321 mCurrentTab = getTabPosition(current);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800322 }
323
Grace Kloba22ac16e2009-10-07 18:00:23 -0700324 // destroy the tab
325 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800326 // clear it's references to parent and children
327 t.removeFromTree();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800328
The Android Open Source Project0c908882009-03-03 19:32:16 -0800329 // Remove it from the queue of viewed tabs.
330 mTabQueue.remove(t);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800331 return true;
332 }
333
334 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800335 * Destroy all the tabs and subwindows
336 */
337 void destroy() {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800338 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700339 t.destroy();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800340 }
341 mTabs.clear();
342 mTabQueue.clear();
343 }
344
345 /**
346 * Returns the number of tabs created.
347 * @return The number of tabs created.
348 */
349 int getTabCount() {
350 return mTabs.size();
351 }
352
The Android Open Source Project0c908882009-03-03 19:32:16 -0800353 /**
Michael Kolbc831b632011-05-11 09:30:34 -0700354 * save the tab state:
355 * current position
356 * position sorted array of tab ids
357 * for each tab id, save the tab state
358 * @param outState
John Reckaed9c542011-05-27 16:08:53 -0700359 * @param saveImages
The Android Open Source Project0c908882009-03-03 19:32:16 -0800360 */
John Reck1cf4b792011-07-26 10:22:22 -0700361 void saveState(Bundle outState) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800362 final int numTabs = getTabCount();
John Reck24f18262011-06-17 14:47:20 -0700363 if (numTabs == 0) {
364 return;
365 }
Michael Kolbc831b632011-05-11 09:30:34 -0700366 long[] ids = new long[numTabs];
367 int i = 0;
368 for (Tab tab : mTabs) {
John Reck1cf4b792011-07-26 10:22:22 -0700369 Bundle tabState = tab.saveState();
Tarun Nainani8eb00912014-07-17 12:28:32 -0700370 if (tabState != null && tab.isPrivateBrowsingEnabled() == false) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700371 ids[i++] = tab.getId();
John Reck52be4782011-08-26 15:37:29 -0700372 String key = Long.toString(tab.getId());
373 if (outState.containsKey(key)) {
374 // Dump the tab state for debugging purposes
375 for (Tab dt : mTabs) {
376 Log.e(LOGTAG, dt.toString());
377 }
378 throw new IllegalStateException(
379 "Error saving state, duplicate tab ids!");
380 }
381 outState.putBundle(key, tabState);
Michael Kolb3ae7f742011-07-13 15:18:25 -0700382 } else {
383 ids[i++] = -1;
John Reck52be4782011-08-26 15:37:29 -0700384 // Since we won't be restoring the thumbnail, delete it
385 tab.deleteThumbnail();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800386 }
387 }
John Reck24f18262011-06-17 14:47:20 -0700388 if (!outState.isEmpty()) {
389 outState.putLongArray(POSITIONS, ids);
390 Tab current = getCurrentTab();
391 long cid = -1;
392 if (current != null) {
393 cid = current.getId();
394 }
395 outState.putLong(CURRENT, cid);
Michael Kolbdbf39812011-06-10 14:06:40 -0700396 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800397 }
398
399 /**
Patrick Scott7d50a932011-02-04 09:27:26 -0500400 * Check if the state can be restored. If the state can be restored, the
Michael Kolbc831b632011-05-11 09:30:34 -0700401 * current tab id is returned. This can be passed to restoreState below
Patrick Scott7d50a932011-02-04 09:27:26 -0500402 * in order to restore the correct tab. Otherwise, -1 is returned and the
403 * state cannot be restored.
404 */
Michael Kolbc831b632011-05-11 09:30:34 -0700405 long canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
406 final long[] ids = (inState == null) ? null : inState.getLongArray(POSITIONS);
407 if (ids == null) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500408 return -1;
409 }
Michael Kolbc831b632011-05-11 09:30:34 -0700410 final long oldcurrent = inState.getLong(CURRENT);
411 long current = -1;
Michael Kolb3ae7f742011-07-13 15:18:25 -0700412 if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) {
John Reck1cf4b792011-07-26 10:22:22 -0700413 current = oldcurrent;
Patrick Scott7d50a932011-02-04 09:27:26 -0500414 } else {
Michael Kolbc831b632011-05-11 09:30:34 -0700415 // pick first non incognito tab
416 for (long id : ids) {
Michael Kolb3ae7f742011-07-13 15:18:25 -0700417 if (hasState(id, inState) && !isIncognito(id, inState)) {
Michael Kolbc831b632011-05-11 09:30:34 -0700418 current = id;
Patrick Scott7d50a932011-02-04 09:27:26 -0500419 break;
420 }
421 }
422 }
Michael Kolbc831b632011-05-11 09:30:34 -0700423 return current;
Patrick Scott7d50a932011-02-04 09:27:26 -0500424 }
425
Michael Kolb3ae7f742011-07-13 15:18:25 -0700426 private boolean hasState(long id, Bundle state) {
427 if (id == -1) return false;
428 Bundle tab = state.getBundle(Long.toString(id));
429 return ((tab != null) && !tab.isEmpty());
430 }
431
432 private boolean isIncognito(long id, Bundle state) {
433 Bundle tabstate = state.getBundle(Long.toString(id));
434 if ((tabstate != null) && !tabstate.isEmpty()) {
435 return tabstate.getBoolean(Tab.INCOGNITO);
436 }
437 return false;
438 }
439
Patrick Scott7d50a932011-02-04 09:27:26 -0500440 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800441 * Restore the state of all the tabs.
Michael Kolbc831b632011-05-11 09:30:34 -0700442 * @param currentId The tab id to restore.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800443 * @param inState The saved state of all the tabs.
Michael Kolb1bf23132010-11-19 12:55:12 -0800444 * @param restoreIncognitoTabs Restoring private browsing tabs
445 * @param restoreAll All webviews get restored, not just the current tab
446 * (this does not override handling of incognito tabs)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800447 */
Michael Kolbc831b632011-05-11 09:30:34 -0700448 void restoreState(Bundle inState, long currentId,
Patrick Scott7d50a932011-02-04 09:27:26 -0500449 boolean restoreIncognitoTabs, boolean restoreAll) {
Michael Kolbc831b632011-05-11 09:30:34 -0700450 if (currentId == -1) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500451 return;
452 }
Michael Kolbc831b632011-05-11 09:30:34 -0700453 long[] ids = inState.getLongArray(POSITIONS);
454 long maxId = -Long.MAX_VALUE;
455 HashMap<Long, Tab> tabMap = new HashMap<Long, Tab>();
456 for (long id : ids) {
457 if (id > maxId) {
458 maxId = id;
459 }
460 final String idkey = Long.toString(id);
461 Bundle state = inState.getBundle(idkey);
John Reckd8c74522011-06-14 08:45:00 -0700462 if (state == null || state.isEmpty()) {
463 // Skip tab
464 continue;
465 } else if (!restoreIncognitoTabs
Michael Kolbc831b632011-05-11 09:30:34 -0700466 && state.getBoolean(Tab.INCOGNITO)) {
467 // ignore tab
468 } else if (id == currentId || restoreAll) {
Axesh R. Ajmerae6b11aa2015-03-16 18:20:41 -0700469 Tab t = null;
470 // Add special check to restore Snapshot Tab if needed
471 if (state.getLong(SnapshotTab.SNAPSHOT_ID, -1) != -1 ) {
Axesh R. Ajmera4b000312015-07-23 10:00:16 -0700472 t = (SnapshotTab) createSnapshotTab( state.getLong(SnapshotTab.SNAPSHOT_ID), state);
Axesh R. Ajmerae6b11aa2015-03-16 18:20:41 -0700473 } else {
474 // presume its a normal Tab
475 t = createNewTab(state, false);
476 }
477
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100478 if (t == null) {
479 // We could "break" at this point, but we want
480 // sNextId to be set correctly.
481 continue;
482 }
Michael Kolbc831b632011-05-11 09:30:34 -0700483 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500484 // Me must set the current tab before restoring the state
485 // so that all the client classes are set.
Michael Kolbc831b632011-05-11 09:30:34 -0700486 if (id == currentId) {
Patrick Scott7d50a932011-02-04 09:27:26 -0500487 setCurrentTab(t);
488 }
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700489 } else {
Patrick Scott7d50a932011-02-04 09:27:26 -0500490 // Create a new tab and don't restore the state yet, add it
491 // to the tab list
John Reck1cf4b792011-07-26 10:22:22 -0700492 Tab t = new Tab(mController, state);
Michael Kolbc831b632011-05-11 09:30:34 -0700493 tabMap.put(id, t);
Patrick Scott7d50a932011-02-04 09:27:26 -0500494 mTabs.add(t);
Enrico Ros1f5a0952014-11-18 20:15:48 -0800495 mTabCountObservable.set(mTabs.size());
Patrick Scott7d50a932011-02-04 09:27:26 -0500496 // added the tab to the front as they are not current
497 mTabQueue.add(0, t);
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700498 }
Michael Kolbc831b632011-05-11 09:30:34 -0700499 }
Narayan Kamath79e5d8d2011-07-20 14:12:38 +0100500
501 // make sure that there is no id overlap between the restored
502 // and new tabs
503 sNextId = maxId + 1;
504
John Reckd8c74522011-06-14 08:45:00 -0700505 if (mCurrentTab == -1) {
506 if (getTabCount() > 0) {
507 setCurrentTab(getTab(0));
John Reckd8c74522011-06-14 08:45:00 -0700508 }
509 }
Michael Kolbc831b632011-05-11 09:30:34 -0700510 // restore parent/child relationships
511 for (long id : ids) {
512 final Tab tab = tabMap.get(id);
513 final Bundle b = inState.getBundle(Long.toString(id));
514 if ((b != null) && (tab != null)) {
515 final long parentId = b.getLong(Tab.PARENTTAB, -1);
516 if (parentId != -1) {
517 final Tab parent = tabMap.get(parentId);
Patrick Scott7d50a932011-02-04 09:27:26 -0500518 if (parent != null) {
Michael Kolbc831b632011-05-11 09:30:34 -0700519 parent.addChildTab(tab);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800520 }
521 }
522 }
523 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800524 }
525
526 /**
Grace Klobaf56f68d2010-04-11 22:53:42 -0700527 * Free the memory in this order, 1) free the background tabs; 2) free the
The Android Open Source Project0c908882009-03-03 19:32:16 -0800528 * WebView cache;
529 */
530 void freeMemory() {
Grace Kloba92c18a52009-07-31 23:48:32 -0700531 if (getTabCount() == 0) return;
532
Grace Klobaf56f68d2010-04-11 22:53:42 -0700533 // free the least frequently used background tabs
534 Vector<Tab> tabs = getHalfLeastUsedTabs(getCurrentTab());
535 if (tabs.size() > 0) {
536 Log.w(LOGTAG, "Free " + tabs.size() + " tabs in the browser");
537 for (Tab t : tabs) {
538 // store the WebView's state.
539 t.saveState();
540 // destroy the tab
541 t.destroy();
542 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800543 return;
544 }
545
Derek Sollenberger4433d032009-06-10 15:37:21 -0400546 // free the WebView's unused memory (this includes the cache)
547 Log.w(LOGTAG, "Free WebView's unused memory and cache");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800548 WebView view = getCurrentWebView();
549 if (view != null) {
Derek Sollenberger4433d032009-06-10 15:37:21 -0400550 view.freeMemory();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800551 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800552 }
553
Grace Klobaf56f68d2010-04-11 22:53:42 -0700554 private Vector<Tab> getHalfLeastUsedTabs(Tab current) {
555 Vector<Tab> tabsToGo = new Vector<Tab>();
556
Patrick Scott2a67de42009-08-31 09:48:37 -0400557 // Don't do anything if we only have 1 tab or if the current tab is
558 // null.
559 if (getTabCount() == 1 || current == null) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700560 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800561 }
562
Grace Klobaf56f68d2010-04-11 22:53:42 -0700563 if (mTabQueue.size() == 0) {
564 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800565 }
566
Grace Klobaf56f68d2010-04-11 22:53:42 -0700567 // Rip through the queue starting at the beginning and tear down half of
568 // available tabs which are not the current tab or the parent of the
569 // current tab.
570 int openTabCount = 0;
571 for (Tab t : mTabQueue) {
572 if (t != null && t.getWebView() != null) {
573 openTabCount++;
Michael Kolbc831b632011-05-11 09:30:34 -0700574 if (t != current && t != current.getParent()) {
Grace Klobaf56f68d2010-04-11 22:53:42 -0700575 tabsToGo.add(t);
576 }
577 }
578 }
579
580 openTabCount /= 2;
581 if (tabsToGo.size() > openTabCount) {
582 tabsToGo.setSize(openTabCount);
583 }
584
585 return tabsToGo;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800586 }
587
Michael Kolbff6a7482011-07-26 16:37:15 -0700588 Tab getLeastUsedTab(Tab current) {
589 if (getTabCount() == 1 || current == null) {
590 return null;
591 }
592 if (mTabQueue.size() == 0) {
593 return null;
594 }
595 // find a tab which is not the current tab or the parent of the
596 // current tab
597 for (Tab t : mTabQueue) {
598 if (t != null && t.getWebView() != null) {
599 if (t != current && t != current.getParent()) {
600 return t;
601 }
602 }
603 }
604 return null;
605 }
606
The Android Open Source Project0c908882009-03-03 19:32:16 -0800607 /**
608 * Show the tab that contains the given WebView.
609 * @param view The WebView used to find the tab.
610 */
611 Tab getTabFromView(WebView view) {
Michael Kolbc831b632011-05-11 09:30:34 -0700612 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700613 if (t.getSubWebView() == view || t.getWebView() == view) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800614 return t;
615 }
616 }
617 return null;
618 }
619
620 /**
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700621 * Return the tab with the matching application id.
622 * @param id The application identifier.
623 */
Michael Kolbc831b632011-05-11 09:30:34 -0700624 Tab getTabFromAppId(String id) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700625 if (id == null) {
626 return null;
627 }
Michael Kolbc831b632011-05-11 09:30:34 -0700628 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700629 if (id.equals(t.getAppId())) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700630 return t;
631 }
632 }
633 return null;
634 }
635
Grace Kloba22ac16e2009-10-07 18:00:23 -0700636 /**
637 * Stop loading in all opened WebView including subWindows.
638 */
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700639 void stopAllLoading() {
Michael Kolbc831b632011-05-11 09:30:34 -0700640 for (Tab t : mTabs) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700641 final WebView webview = t.getWebView();
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700642 if (webview != null) {
643 webview.stopLoading();
644 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700645 final WebView subview = t.getSubWebView();
646 if (subview != null) {
Mattias Nilsson9727af82012-06-14 17:07:56 +0200647 subview.stopLoading();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700648 }
Grace Kloba5d0e02e2009-10-05 15:15:36 -0700649 }
650 }
651
John Reckdb22ec42011-06-29 11:31:24 -0700652 // This method checks if a tab matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400653 private boolean tabMatchesUrl(Tab t, String url) {
John Reckdb22ec42011-06-29 11:31:24 -0700654 return url.equals(t.getUrl()) || url.equals(t.getOriginalUrl());
Patrick Scottcd115892009-07-16 09:42:58 -0400655 }
656
657 /**
John Reckdb22ec42011-06-29 11:31:24 -0700658 * Return the tab that matches the given url.
Patrick Scottcd115892009-07-16 09:42:58 -0400659 * @param url The url to search for.
660 */
John Reckdb22ec42011-06-29 11:31:24 -0700661 Tab findTabWithUrl(String url) {
Patrick Scottcd115892009-07-16 09:42:58 -0400662 if (url == null) {
663 return null;
664 }
665 // Check the current tab first.
John Reckdb22ec42011-06-29 11:31:24 -0700666 Tab currentTab = getCurrentTab();
667 if (currentTab != null && tabMatchesUrl(currentTab, url)) {
668 return currentTab;
Patrick Scottcd115892009-07-16 09:42:58 -0400669 }
670 // Now check all the rest.
Michael Kolbc831b632011-05-11 09:30:34 -0700671 for (Tab tab : mTabs) {
672 if (tabMatchesUrl(tab, url)) {
John Reckdb22ec42011-06-29 11:31:24 -0700673 return tab;
Patrick Scottcd115892009-07-16 09:42:58 -0400674 }
675 }
676 return null;
677 }
678
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700679 /**
John Reck30c714c2010-12-16 17:30:34 -0800680 * Recreate the main WebView of the given tab.
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700681 */
John Reck30c714c2010-12-16 17:30:34 -0800682 void recreateWebView(Tab t) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700683 final WebView w = t.getWebView();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700684 if (w != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700685 t.destroy();
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700686 }
687 // Create a new WebView. If this tab is the current tab, we need to put
688 // back all the clients so force it to be the current tab.
Panos Thomasa9a5a582014-03-18 19:20:08 -0700689 t.setWebView(createNewWebView(t.isPrivateBrowsingEnabled()), false);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700690 if (getCurrentTab() == t) {
691 setCurrentTab(t, true);
692 }
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700693 }
694
695 /**
696 * Creates a new WebView and registers it with the global settings.
697 */
698 private WebView createNewWebView() {
Elliott Slaughterf0f03952010-07-14 18:10:36 -0700699 return createNewWebView(false);
700 }
701
702 /**
703 * Creates a new WebView and registers it with the global settings.
704 * @param privateBrowsing When true, enables private browsing in the new
705 * WebView.
706 */
707 private WebView createNewWebView(boolean privateBrowsing) {
Stewart Chaoe0e132e2014-12-01 18:28:22 -0500708 return createNewWebView(privateBrowsing, false);
709 }
710
711 private WebView createNewWebView(boolean privateBrowsing, boolean backgroundTab) {
712 return mController.getWebViewFactory().createWebView(privateBrowsing, backgroundTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700713 }
714
715 /**
The Android Open Source Project0c908882009-03-03 19:32:16 -0800716 * Put the current tab in the background and set newTab as the current tab.
717 * @param newTab The new tab. If newTab is null, the current tab is not
718 * set.
719 */
720 boolean setCurrentTab(Tab newTab) {
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700721 return setCurrentTab(newTab, false);
722 }
723
724 /**
725 * If force is true, this method skips the check for newTab == current.
726 */
727 private boolean setCurrentTab(Tab newTab, boolean force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800728 Tab current = getTab(mCurrentTab);
The Android Open Source Projectf59ec872009-03-13 13:04:24 -0700729 if (current == newTab && !force) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800730 return true;
731 }
732 if (current != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700733 current.putInBackground();
734 mCurrentTab = -1;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800735 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800736 if (newTab == null) {
737 return false;
738 }
739
740 // Move the newTab to the end of the queue
741 int index = mTabQueue.indexOf(newTab);
742 if (index != -1) {
743 mTabQueue.remove(index);
744 }
745 mTabQueue.add(newTab);
746
The Android Open Source Project0c908882009-03-03 19:32:16 -0800747 // Display the new current tab
748 mCurrentTab = mTabs.indexOf(newTab);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700749 WebView mainView = newTab.getWebView();
John Reckd8c74522011-06-14 08:45:00 -0700750 boolean needRestore = !newTab.isSnapshot() && (mainView == null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800751 if (needRestore) {
752 // Same work as in createNewTab() except don't do new Tab()
Panos Thomasa9a5a582014-03-18 19:20:08 -0700753 mainView = createNewWebView(newTab.isPrivateBrowsingEnabled());
Steve Block2bc69912009-07-30 14:45:13 +0100754 newTab.setWebView(mainView);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800755 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700756 newTab.putInForeground();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800757 return true;
758 }
Michael Kolbfe251992010-07-08 15:41:55 -0700759
John Reck8ee633f2011-08-09 16:00:35 -0700760 public void setOnThumbnailUpdatedListener(OnThumbnailUpdatedListener listener) {
761 mOnThumbnailUpdatedListener = listener;
762 for (Tab t : mTabs) {
763 WebView web = t.getWebView();
764 if (web != null) {
765 web.setPictureListener(listener != null ? t : null);
766 }
767 }
768 }
769
770 public OnThumbnailUpdatedListener getOnThumbnailUpdatedListener() {
771 return mOnThumbnailUpdatedListener;
772 }
773
The Android Open Source Project0c908882009-03-03 19:32:16 -0800774}