Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(381)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelImpl.java

Issue 802343003: Upstream DocumentTabModelImpl and related classes (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Moved package, added OWNERS Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.tabmodel.document;
6
7 import android.annotation.TargetApi;
8 import android.app.Activity;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.content.SharedPreferences;
12 import android.os.AsyncTask;
13 import android.os.Build;
14 import android.text.TextUtils;
15 import android.util.Log;
16 import android.util.Pair;
17 import android.util.SparseArray;
18
19 import com.google.protobuf.nano.MessageNano;
20
21 import org.chromium.base.ApplicationStatus;
22 import org.chromium.base.ObserverList;
23 import org.chromium.base.ThreadUtils;
24 import org.chromium.base.VisibleForTesting;
25 import org.chromium.chrome.browser.Tab;
26 import org.chromium.chrome.browser.TabState;
27 import org.chromium.chrome.browser.tabmodel.TabList;
28 import org.chromium.chrome.browser.tabmodel.TabModel;
29 import org.chromium.chrome.browser.tabmodel.TabModelJniBridge;
30 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
31 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
32 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelInfo.Docume ntEntry;
33 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelInfo.Docume ntList;
34 import org.chromium.chrome.browser.util.MathUtils;
35
36 import java.io.File;
37 import java.io.IOException;
38 import java.lang.ref.WeakReference;
39 import java.util.ArrayList;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Set;
43
44 /**
45 * Maintains a list of Tabs displayed when Chrome is running in document-mode.
46 */
47 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
48 public class DocumentTabModelImpl extends TabModelJniBridge implements DocumentT abModel {
49 private static final String TAG = "DocumentTabModel";
50
51 @VisibleForTesting
52 public static final String PREF_PACKAGE = "com.google.android.apps.chrome.do cument";
53
54 @VisibleForTesting
55 public static final String PREF_LAST_SHOWN_TAB_ID_REGULAR = "last_shown_tab_ id.regular";
56
57 public static final String PREF_LAST_SHOWN_TAB_ID_INCOGNITO = "last_shown_ta b_id.incognito";
58
59 /** TabModel is uninitialized. */
60 public static final int STATE_UNINITIALIZED = 0;
61
62 /** Begin parsing the tasks from Recents and loading persisted state. */
63 public static final int STATE_READ_RECENT_TASKS_START = 1;
64
65 /** Done parsing the tasks from Recents and loading persisted state. */
66 public static final int STATE_READ_RECENT_TASKS_END = 2;
67
68 /** Begin loading the current/prioritized tab state synchronously. */
69 public static final int STATE_LOAD_CURRENT_TAB_STATE_START = 3;
70
71 /** Finish loading the current/prioritized tab state synchronously. */
72 public static final int STATE_LOAD_CURRENT_TAB_STATE_END = 4;
73
74 /** Begin reading TabStates from storage for background tabs. */
75 public static final int STATE_LOAD_TAB_STATE_BG_START = 5;
76
77 /** Done reading TabStates from storage for background tabs. */
78 public static final int STATE_LOAD_TAB_STATE_BG_END = 6;
79
80 /** Begin deserializing the TabState. Requires the native library. */
81 public static final int STATE_DESERIALIZE_START = 7;
82
83 /** Done deserializing the TabState. */
84 public static final int STATE_DESERIALIZE_END = 8;
85
86 /** Begin parsing the historical tabs. */
87 public static final int STATE_DETERMINE_HISTORICAL_TABS_START = 9;
88
89 /** Done parsing the historical tabs. */
90 public static final int STATE_DETERMINE_HISTORICAL_TABS_END = 10;
91
92 /** Clean out old TabState files. */
93 public static final int STATE_CLEAN_UP_OBSOLETE_TABS = 11;
94
95 /** TabModel is fully ready to use. */
96 public static final int STATE_FULLY_LOADED = 12;
97
98 /** List of known tabs. */
99 private final ArrayList<Integer> mTabIdList;
100
101 /** Stores an entry for each DocumentActivity that is alive. Keys are docum ent IDs. */
102 private final SparseArray<Entry> mEntryMap;
103
104 /**
105 * Stores tabIds which have been removed from the ActivityManager while Chro me was not alive.
106 * It is cleared after restoration has been finished.
107 */
108 private final List<Integer> mHistoricalTabs;
109
110 /** Delegate for working with the ActivityManager. */
111 private final ActivityDelegate mActivityDelegate;
112
113 /** Delegate for working with the filesystem. */
114 private final StorageDelegate mStorageDelegate;
115
116 /** Delegate that provides Tabs to the DocumentTabModel. */
117 private final TabDelegate mTabDelegate;
118
119 /** ID of a Tab whose state should be loaded immediately, if it belongs to t his TabList. */
120 private final int mPrioritizedTabId;
121
122 /** List of observers watching for a particular loading state. */
123 private final ObserverList<InitializationObserver> mInitializationObservers;
124
125 /** List of observers watching the TabModel. */
126 private final ObserverList<TabModelObserver> mObservers;
127
128 /** Context to use. */
129 private final Context mContext;
130
131 /** Current loading status. */
132 private int mCurrentState;
133
134 /** ID of the last tab that was shown to the user. */
135 private int mLastShownTabId = Tab.INVALID_TAB_ID;
136
137 /**
138 * Construct a DocumentTabModelImpl.
139 * @param activityDelegate Used to interact with DocumentActivities.
140 * @param tabDelegate Used to create/get Tabs.
141 * @param isIncognito Whether or not the TabList is managing incognito tabs.
142 * @param prioritizedTabId ID of the tab to prioritize when loading.
143 */
144 public DocumentTabModelImpl(ActivityDelegate activityDelegate, TabDelegate t abDelegate,
145 boolean isIncognito, int prioritizedTabId) {
146 this(activityDelegate, new StorageDelegate(isIncognito), tabDelegate, is Incognito,
147 prioritizedTabId, ApplicationStatus.getApplicationContext());
148 }
149
150 /**
151 * Construct a DocumentTabModel.
152 * @param activityDelegate Delegate to use for accessing the ActivityManager .
153 * @param storageDelegate Delegate to use for accessing persistent storage.
154 * @param tabDelegate Used to create/get Tabs.
155 * @param isIncognito Whether or not the TabList is managing incognito tabs.
156 * @param prioritizedTabId ID of the tab to prioritize when loading.
157 * @param context Context to use for accessing SharedPreferences.
158 *
159 * TODO(dfalcantara): Reduce visibility once DocumentMigrationHelper is upst reamed.
160 */
161 public DocumentTabModelImpl(ActivityDelegate activityDelegate, StorageDelega te storageDelegate,
162 TabDelegate tabDelegate, boolean isIncognito, int prioritizedTabId, Context context) {
163 super(isIncognito);
164 mActivityDelegate = activityDelegate;
165 mStorageDelegate = storageDelegate;
166 mTabDelegate = tabDelegate;
167 mPrioritizedTabId = prioritizedTabId;
168 mContext = context;
169
170 mCurrentState = STATE_UNINITIALIZED;
171 mTabIdList = new ArrayList<Integer>();
172 mEntryMap = new SparseArray<Entry>();
173 mHistoricalTabs = new ArrayList<Integer>();
174 mInitializationObservers = new ObserverList<InitializationObserver>();
175 mObservers = new ObserverList<TabModelObserver>();
176
177 SharedPreferences prefs = mContext.getSharedPreferences(PREF_PACKAGE, Co ntext.MODE_PRIVATE);
178 mLastShownTabId = prefs.getInt(
179 isIncognito() ? PREF_LAST_SHOWN_TAB_ID_INCOGNITO : PREF_LAST_SHO WN_TAB_ID_REGULAR,
180 Tab.INVALID_TAB_ID);
181
182 initializeTabList();
183 }
184
185 @Override
186 public void initializeNative() {
187 if (!isNativeInitialized()) super.initializeNative();
188 deserializeTabStatesAsync();
189 }
190
191 public StorageDelegate getStorageDelegate() {
192 return mStorageDelegate;
193 }
194
195 /**
196 * Finds the index of the given Tab ID.
197 * @param tabId ID of the Tab to find.
198 * @return Index of the tab, or -1 if it couldn't be found.
199 */
200 private int indexOf(int tabId) {
201 return mTabIdList.indexOf(tabId);
202 }
203
204 @Override
205 public int index() {
206 if (getCount() == 0) return TabList.INVALID_TAB_INDEX;
207 int indexOfLastId = indexOf(mLastShownTabId);
208 if (indexOfLastId != -1) return indexOfLastId;
209
210 // The previous Tab is gone; select a Tab based on MRU ordering.
211 List<Entry> tasks = mActivityDelegate.getTasksFromRecents(isIncognito()) ;
212 if (tasks.size() == 0) return TabList.INVALID_TAB_INDEX;
213
214 for (int i = 0; i < tasks.size(); i++) {
215 int lastKnownId = tasks.get(i).tabId;
216 int indexOfMostRecentlyUsedId = indexOf(lastKnownId);
217 if (indexOfMostRecentlyUsedId != -1) return indexOfMostRecentlyUsedI d;
218 }
219
220 return TabList.INVALID_TAB_INDEX;
221 }
222
223 @Override
224 public void setLastShownId(int id) {
225 mLastShownTabId = id;
226
227 String prefName =
228 isIncognito() ? PREF_LAST_SHOWN_TAB_ID_INCOGNITO : PREF_LAST_SHO WN_TAB_ID_REGULAR;
229 SharedPreferences prefs = mContext.getSharedPreferences(PREF_PACKAGE, Co ntext.MODE_PRIVATE);
230 SharedPreferences.Editor editor = prefs.edit();
231 editor.putInt(prefName, id);
232 editor.apply();
233 }
234
235 @Override
236 public int indexOf(Tab tab) {
237 if (tab == null) return Tab.INVALID_TAB_ID;
238 return indexOf(tab.getId());
239 }
240
241 @Override
242 public int getCount() {
243 return mTabIdList.size();
244 }
245
246 @Override
247 public boolean isClosurePending(int tabId) {
248 return false;
249 }
250
251 @Override
252 public Tab getTabAt(int index) {
253 if (index < 0 || index >= getCount()) return null;
254
255 // Return a live tab if the corresponding Activity is currently alive.
256 int tabId = mTabIdList.get(index);
257 List<WeakReference<Activity>> activities = ApplicationStatus.getRunningA ctivities();
258 for (WeakReference<Activity> activityRef : activities) {
259 Tab tab = mTabDelegate.getActivityTab(
260 isIncognito(), mActivityDelegate, activityRef.get());
261 int documentId = tab == null ? Tab.INVALID_TAB_ID : tab.getId();
262 if (documentId == tabId) return tab;
263 }
264
265 // Try to create a Tab that will hold the Tab's info.
266 Entry entry = mEntryMap.get(tabId);
267 if (entry == null) return null;
268
269 // If a tab has already been initialized, use that.
270 if (entry.placeholderTab != null && entry.placeholderTab.isInitialized() ) {
271 return entry.placeholderTab;
272 }
273
274 // Create a frozen Tab if we are capable, or if the previous Tab is just a placeholder.
275 if (entry.tabState != null && isNativeInitialized()
276 && (entry.placeholderTab == null || !entry.placeholderTab.isInit ialized())) {
277 entry.placeholderTab = mTabDelegate.createFrozenTab(entry);
278 entry.placeholderTab.initialize();
279 }
280
281 // Create a placeholder Tab that just has the ID.
282 if (entry.placeholderTab == null) {
283 entry.placeholderTab = new Tab(tabId, isIncognito(), null, null);
284 }
285
286 return entry.placeholderTab;
287 }
288
289 @Override
290 public void setIndex(int index, TabSelectionType type) {
291 if (index < 0 || index >= getCount()) return;
292 int tabId = mTabIdList.get(index);
293 mActivityDelegate.moveTaskToFront(isIncognito(), tabId);
294 setLastShownId(tabId);
295 }
296
297 @Override
298 public boolean closeTabAt(int index) {
299 ThreadUtils.assertOnUiThread();
300 if (index < 0 || index >= getCount()) return false;
301
302 int tabId = mTabIdList.get(index);
303 mActivityDelegate.finishAndRemoveTask(isIncognito(), tabId);
304 mTabIdList.remove(index);
305 mEntryMap.remove(tabId);
306 return true;
307 }
308
309 @Override
310 public boolean closeTab(Tab tab) {
311 return closeTab(tab, false, false, false);
312 }
313
314 @Override
315 public boolean closeTab(Tab tabToClose, boolean animate, boolean uponExit, b oolean canUndo) {
316 // The tab should be destroyed by the DocumentActivity that owns it.
317 return closeTabAt(indexOf(tabToClose.getId()));
318 }
319
320 @Override
321 protected Tab createTabWithNativeContents(
322 boolean isIncognito, long webContentsPtr, int parentTabId) {
323 mTabDelegate.createTabWithNativeContents(isIncognito, webContentsPtr, pa rentTabId);
324 return null;
325 }
326
327 @Override
328 protected Tab createNewTabForDevTools(String url) {
329 mTabDelegate.createTabForDevTools(url);
330 return null;
331 }
332
333 @Override
334 protected boolean isSessionRestoreInProgress() {
335 return mCurrentState < STATE_FULLY_LOADED;
336 }
337
338 /**
339 * Add the tab ID to the end of the list.
340 * @param tabId ID to add.
341 */
342 private void addTabId(int tabId) {
343 addTabId(mTabIdList.size(), tabId);
344 }
345
346 /**
347 * Adds the Tab ID at the given index.
348 * @param index Where to add the ID.
349 * @param tabId ID to add.
350 */
351 private void addTabId(int index, int tabId) {
352 if (mTabIdList.contains(tabId)) return;
353 mTabIdList.add(index, tabId);
354 }
355
356 @Override
357 public String getInitialUrlForDocument(int tabId) {
358 Entry entry = mEntryMap.get(tabId);
359 return entry == null ? null : entry.initialUrl;
360 }
361
362 @Override
363 public String getCurrentUrlForDocument(int tabId) {
364 Entry entry = mEntryMap.get(tabId);
365 return entry == null ? null : entry.currentUrl;
366 }
367
368 @Override
369 public boolean isTabStateReady(int tabId) {
370 Entry entry = mEntryMap.get(tabId);
371 return entry == null ? true : entry.isTabStateReady;
372 }
373
374 @Override
375 public TabState getTabStateForDocument(int tabId) {
376 Entry entry = mEntryMap.get(tabId);
377 return entry == null ? null : entry.tabState;
378 }
379
380 @Override
381 public boolean hasEntryForTabId(int tabId) {
382 return mEntryMap.get(tabId) != null;
383 }
384
385 @Override
386 public boolean isRetargetable(int tabId) {
387 Entry entry = mEntryMap.get(tabId);
388 return entry == null ? false : !entry.canGoBack;
389 }
390
391 @Override
392 public void addInitializationObserver(InitializationObserver observer) {
393 ThreadUtils.assertOnUiThread();
394 mInitializationObservers.addObserver(observer);
395 }
396
397 @Override
398 public void updateRecentlyClosed() {
399 ThreadUtils.assertOnUiThread();
400 List<Entry> current = mActivityDelegate.getTasksFromRecents(isIncognito( ));
401 Set<Integer> removed = new HashSet<Integer>();
402 for (int i = 0; i < mEntryMap.size(); i++) {
403 int tabId = mEntryMap.keyAt(i);
404 if (!isTabIdInEntryList(current, tabId)) {
405 Entry entry = mEntryMap.get(tabId);
406 if (!isIncognito() && entry.tabState != null
407 && entry.tabState.contentsState != null) {
408 entry.tabState.contentsState.createHistoricalTab();
409 }
410 removed.add(tabId);
411 }
412 }
413
414 for (Integer tabId : removed) {
415 closeTabAt(indexOf(tabId));
416 }
417 }
418
419 @Override
420 public void updateEntry(Intent intent, Tab tab) {
421 if (!mActivityDelegate.isValidActivity(isIncognito(), intent)) return;
422
423 int id = ActivityDelegate.getTabIdFromIntent(intent);
424 if (id == Tab.INVALID_TAB_ID) return;
425
426 Entry currentEntry = mEntryMap.get(id);
427 String currentUrl = tab.getUrl();
428 boolean canGoBack = tab.canGoBack();
429 TabState state = tab.getState();
430 if (currentEntry != null
431 && currentEntry.tabId == id
432 && TextUtils.equals(currentEntry.currentUrl, currentUrl)
433 && currentEntry.canGoBack == canGoBack
434 && currentEntry.tabState == state
435 && !tab.isTabStateDirty()) {
436 return;
437 }
438
439 if (currentEntry == null) {
440 currentEntry = new Entry(id, ActivityDelegate.getInitialUrlForDocume nt(intent));
441 mEntryMap.put(id, currentEntry);
442 }
443 currentEntry.isDirty = true;
444 currentEntry.currentUrl = currentUrl;
445 currentEntry.canGoBack = canGoBack;
446 currentEntry.tabState = state;
447
448 // TODO(dfalcantara): This is different from how the normal Tab determin es when to save its
449 // state, but this can't be fixed because we cann't hold onto Tabs in th is class.
450 tab.setIsTabStateDirty(false);
451
452 if (currentEntry.placeholderTab != null) {
453 if (currentEntry.placeholderTab.isInitialized()) currentEntry.placeh olderTab.destroy();
454 currentEntry.placeholderTab = null;
455 }
456
457 writeGeneralDataToStorageAsync();
458 writeTabStatesToStorageAsync();
459 }
460
461 @Override
462 public int getCurrentInitializationStage() {
463 return mCurrentState;
464 }
465
466 /**
467 * Add an entry to the entry map for migration purposes.
468 * @param entry The entry to be added.
469 *
470 * TODO(dfalcantara): Reduce visibility once DocumentMigrationHelper is upst reamed.
471 */
472 public void addEntryForMigration(Entry entry) {
473 addTabId(getCount(), entry.tabId);
474 if (mEntryMap.indexOfKey(entry.tabId) >= 0) return;
475 mEntryMap.put(entry.tabId, entry);
476 }
477
478 private void initializeTabList() {
479 setCurrentState(STATE_READ_RECENT_TASKS_START);
480
481 // Run through Recents to see what tasks exist. Prevent them from being retargeted until we
482 // have had the opportunity to load more information about them.
483 List<Entry> entries = mActivityDelegate.getTasksFromRecents(isIncognito( ));
484 for (Entry entry : entries) {
485 entry.canGoBack = true;
486 mEntryMap.put(entry.tabId, entry);
487 }
488
489 // Read the file, which saved out the task IDs in regular order.
490 byte[] tabFileBytes = mStorageDelegate.readTaskFileBytes();
491 if (tabFileBytes != null) {
492 try {
493 DocumentList list = MessageNano.mergeFrom(new DocumentList(), ta bFileBytes);
494 for (int i = 0; i < list.entries.length; i++) {
495 DocumentEntry savedEntry = list.entries[i];
496 int tabId = savedEntry.tabId;
497
498 if (mEntryMap.indexOfKey(tabId) < 0) {
499 mHistoricalTabs.add(tabId);
500 continue;
501 }
502
503 addTabId(getCount(), tabId);
504 mEntryMap.get(tabId).canGoBack = savedEntry.canGoBack;
505 }
506 } catch (IOException e) {
507 Log.e(TAG, "I/O exception", e);
508 }
509 }
510
511 // Add any missing tasks to the list.
512 for (int i = 0; i < mEntryMap.size(); i++) {
513 int id = mEntryMap.keyAt(i);
514 if (mTabIdList.contains(id)) continue;
515 addTabId(id);
516 }
517
518 setCurrentState(STATE_READ_RECENT_TASKS_END);
519 }
520
521 // TODO(mariakhomenko): we no longer need prioritized tab id in constructor, shift it here.
522 @Override
523 public void startTabStateLoad() {
524 if (mCurrentState != STATE_READ_RECENT_TASKS_END) return;
525 setCurrentState(STATE_LOAD_CURRENT_TAB_STATE_START);
526 // Immediately try loading the requested tab.
527 if (mPrioritizedTabId != Tab.INVALID_TAB_ID) {
528 Entry entry = mEntryMap.get(mPrioritizedTabId);
529 if (entry != null) {
530 entry.tabState = mStorageDelegate.restoreTabState(mPrioritizedTa bId);
531 entry.isTabStateReady = true;
532 }
533 }
534 setCurrentState(STATE_LOAD_CURRENT_TAB_STATE_END);
535 loadTabStatesAsync();
536 }
537
538 private void loadTabStatesAsync() {
539 new AsyncTask<Void, Void, Void>() {
540 private final List<Entry> mEntries = new ArrayList<Entry>(getCount() );
541
542 @Override
543 public void onPreExecute() {
544 setCurrentState(STATE_LOAD_TAB_STATE_BG_START);
545 for (int i = 0; i < getCount(); i++) {
546 mEntries.add(new Entry(getTabAt(i).getId()));
547 }
548 }
549
550 @Override
551 public Void doInBackground(Void... params) {
552 for (Entry entry : mEntries) {
553 if (mPrioritizedTabId == entry.tabId) continue;
554 entry.tabState = mStorageDelegate.restoreTabState(entry.tabI d);
555 entry.isTabStateReady = true;
556 }
557
558 return null;
559 }
560
561 @Override
562 public void onPostExecute(Void result) {
563 for (Entry pair : mEntries) {
564 Entry entry = mEntryMap.get(pair.tabId);
565 if (entry == null) continue;
566
567 if (entry.tabState == null) entry.tabState = pair.tabState;
568 entry.isTabStateReady = true;
569 }
570
571 setCurrentState(STATE_LOAD_TAB_STATE_BG_END);
572 deserializeTabStatesAsync();
573 }
574 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
575 }
576
577 private void deserializeTabStatesAsync() {
578 if (!shouldStartDeserialization(mCurrentState)) return;
579
580 new AsyncTask<Void, Void, Void>() {
581 private final List<Entry> mCachedEntries = new ArrayList<Entry>(mEnt ryMap.size());
582
583 @Override
584 public void onPreExecute() {
585 setCurrentState(STATE_DESERIALIZE_START);
586
587 for (int i = 0; i < mEntryMap.size(); i++) {
588 Entry entry = mEntryMap.valueAt(i);
589 if (entry.tabState == null) continue;
590 mCachedEntries.add(new Entry(entry.tabId, entry.tabState));
591 }
592 }
593
594 @Override
595 public Void doInBackground(Void... params) {
596 for (Entry entry : mCachedEntries) {
597 TabState tabState = entry.tabState;
598 updateEntryInfoFromTabState(entry, tabState);
599 }
600 return null;
601 }
602
603 @Override
604 public void onPostExecute(Void result) {
605 for (Entry pair : mCachedEntries) {
606 Entry realEntry = mEntryMap.get(pair.tabId);
607 if (realEntry == null || realEntry.currentUrl != null) conti nue;
608 realEntry.currentUrl = pair.currentUrl;
609 }
610
611 setCurrentState(STATE_DESERIALIZE_END);
612 if (isNativeInitialized()) {
613 broadcastSessionRestoreComplete();
614 loadHistoricalTabsAsync();
615 }
616 }
617 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
618 }
619
620 /**
621 * Call for extending classes to override for getting additional information for an entry from
622 * the tab state when it is deserialized.
623 * @param entry The {@link Entry} currently being processed
624 * @param tabState The {@link TabState} that has been deserialized for the e ntry.
625 */
626 protected void updateEntryInfoFromTabState(Entry entry, TabState tabState) {
627 entry.currentUrl = tabState.getVirtualUrlFromState();
628 }
629
630 /**
631 * Checks whether initialization should move to the deserialization step.
632 * @param currentState Current initialization stage.
633 * @return Whether to proceed or not.
634 */
635 protected boolean shouldStartDeserialization(int currentState) {
636 return isNativeInitialized() && currentState == STATE_LOAD_TAB_STATE_BG_ END;
637 }
638
639 private void loadHistoricalTabsAsync() {
640 new AsyncTask<Void, Void, Void>() {
641 private Set<Integer> mHistoricalTabsForBackgroundThread;
642 private List<Entry> mEntries;
643
644 @Override
645 public void onPreExecute() {
646 setCurrentState(STATE_DETERMINE_HISTORICAL_TABS_START);
647 mHistoricalTabsForBackgroundThread = new HashSet<Integer>(mHisto ricalTabs.size());
648 mHistoricalTabsForBackgroundThread.addAll(mHistoricalTabs);
649 mEntries = new ArrayList<Entry>(mHistoricalTabsForBackgroundThre ad.size());
650 }
651
652 @Override
653 public Void doInBackground(Void... params) {
654 for (Integer tabId : mHistoricalTabsForBackgroundThread) {
655 // Read the saved state, then delete the file.
656 TabState state = mStorageDelegate.restoreTabState(tabId);
657 mEntries.add(new Entry(tabId, state));
658 mStorageDelegate.deleteTabStateFile(tabId);
659 }
660
661 return null;
662 }
663
664 @Override
665 public void onPostExecute(Void result) {
666 for (Entry entry : mEntries) {
667 if (entry.tabState == null || entry.tabState.contentsState = = null) continue;
668 entry.tabState.contentsState.createHistoricalTab();
669 }
670 mHistoricalTabs.clear();
671 setCurrentState(STATE_DETERMINE_HISTORICAL_TABS_END);
672 cleanUpObsoleteTabStatesAsync();
673 }
674 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
675 }
676
677 /**
678 * Clears the folder of TabStates that correspond to missing tasks.
679 */
680 private void cleanUpObsoleteTabStatesAsync() {
681 new AsyncTask<Void, Void, Void>() {
682 private List<Entry> mCurrentTabs;
683
684 @Override
685 protected void onPreExecute() {
686 setCurrentState(STATE_CLEAN_UP_OBSOLETE_TABS);
687 mCurrentTabs = mActivityDelegate.getTasksFromRecents(isIncognito ());
688 }
689
690 @Override
691 protected Void doInBackground(Void... voids) {
692 File stateDirectory = mStorageDelegate.getStateDirectory();
693 String[] files = stateDirectory.list();
694 for (final String fileName : files) {
695 Pair<Integer, Boolean> tabInfo = TabState.parseInfoFromFilen ame(fileName);
696 if (tabInfo == null) continue;
697
698 int tabId = tabInfo.first;
699 boolean incognito = tabInfo.second;
700 if (incognito != isIncognito() || isTabIdInEntryList(mCurren tTabs, tabId)) {
701 continue;
702 }
703
704 boolean success = new File(stateDirectory, fileName).delete( );
705 if (!success) Log.w(TAG, "Failed to delete: " + fileName);
706 }
707
708 return null;
709 }
710
711 @Override
712 protected void onPostExecute(Void result) {
713 setCurrentState(STATE_FULLY_LOADED);
714 }
715 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
716 }
717
718 /**
719 * Save out a tiny file with minimal information required for retargeting.
720 */
721 private void writeGeneralDataToStorageAsync() {
722 if (isIncognito()) return;
723
724 new AsyncTask<Void, Void, Void>() {
725 private DocumentList mList;
726
727 @Override
728 protected void onPreExecute() {
729 List<DocumentEntry> entriesList = new ArrayList<DocumentEntry>() ;
730 for (int i = 0; i < getCount(); i++) {
731 Entry entry = mEntryMap.get(getTabAt(i).getId());
732 if (entry == null) continue;
733
734 DocumentEntry docEntry = new DocumentEntry();
735 docEntry.tabId = entry.tabId;
736 docEntry.canGoBack = entry.canGoBack;
737
738 entriesList.add(docEntry);
739 }
740 mList = new DocumentList();
741 mList.entries = entriesList.toArray(new DocumentEntry[entriesLis t.size()]);
742 }
743
744 @Override
745 protected Void doInBackground(Void... params) {
746 mStorageDelegate.writeTaskFileBytes(MessageNano.toByteArray(mLis t));
747 return null;
748 }
749 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
750 }
751
752 /**
753 * Write out all of the TabStates.
754 */
755 private void writeTabStatesToStorageAsync() {
756 new AsyncTask<Void, Void, Void>() {
757 private final SparseArray<TabState> mStatesToWrite = new SparseArray <TabState>();
758
759 @Override
760 protected void onPreExecute() {
761 for (int i = 0; i < mEntryMap.size(); i++) {
762 Entry entry = mEntryMap.valueAt(i);
763 if (!entry.isDirty || entry.tabState == null) continue;
764 mStatesToWrite.put(entry.tabId, entry.tabState);
765 }
766 }
767
768 @Override
769 protected Void doInBackground(Void... voids) {
770 for (int i = 0; i < mStatesToWrite.size(); i++) {
771 int tabId = mStatesToWrite.keyAt(i);
772 mStorageDelegate.saveTabState(tabId, mStatesToWrite.valueAt( i));
773 }
774 return null;
775 }
776
777 @Override
778 protected void onPostExecute(Void v) {
779 for (int i = 0; i < mStatesToWrite.size(); i++) {
780 int tabId = mStatesToWrite.keyAt(i);
781 Entry entry = mEntryMap.get(tabId);
782 if (entry == null) continue;
783 entry.isDirty = false;
784 }
785 }
786 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
787 }
788
789 private void setCurrentState(int newState) {
790 ThreadUtils.assertOnUiThread();
791 assert mCurrentState == newState - 1;
792 mCurrentState = newState;
793
794 for (InitializationObserver observer : mInitializationObservers) {
795 if (observer.isCanceled()) {
796 Log.w(TAG, "Observer alerted after canceled: " + observer);
797 mInitializationObservers.removeObserver(observer);
798 } else if (observer.isSatisfied(mCurrentState)) {
799 observer.runWhenReady();
800 mInitializationObservers.removeObserver(observer);
801 }
802 }
803 }
804
805 @Override
806 public Tab getNextTabIfClosed(int id) {
807 // Tab may not necessarily exist.
808 return null;
809 }
810
811 @Override
812 public void closeAllTabs() {
813 closeAllTabs(true, false);
814 }
815
816 @Override
817 public void closeAllTabs(boolean allowDelegation, boolean uponExit) {
818 for (int i = getCount() - 1; i >= 0; i--) closeTabAt(i);
819 }
820
821 @Override
822 public void moveTab(int id, int newIndex) {
823 newIndex = MathUtils.clamp(newIndex, 0, getCount());
824 int curIndex = TabModelUtils.getTabIndexById(this, id);
825 if (curIndex == INVALID_TAB_INDEX || curIndex == newIndex || curIndex + 1 == newIndex) {
826 return;
827 }
828
829 mTabIdList.remove(curIndex);
830 addTabId(newIndex, id);
831
832 Tab tab = getTabAt(curIndex);
833 if (tab == null) return;
834 for (TabModelObserver obs : mObservers) obs.didMoveTab(tab, newIndex, cu rIndex);
835 }
836
837 @Override
838 public void destroy() {
839 super.destroy();
840 mInitializationObservers.clear();
841 mObservers.clear();
842 }
843
844 @Override
845 public void addTab(Tab tab) {
846 int parentIndex = indexOf(tab.getParentId());
847 int index = parentIndex == -1 ? getCount() : parentIndex + 1;
848 addTab(tab, index, tab.getLaunchType());
849 }
850
851 @Override
852 public void addTab(Tab tab, int index, TabLaunchType type) {
853 for (TabModelObserver obs : mObservers) obs.willAddTab(tab, type);
854
855 if (index == TabModel.INVALID_TAB_INDEX) {
856 addTabId(getCount(), tab.getId());
857 } else {
858 addTabId(index, tab.getId());
859 }
860
861 tabAddedToModel(tab);
862 for (TabModelObserver obs : mObservers) obs.didAddTab(tab, type);
863 }
864
865 @Override
866 public boolean supportsPendingClosures() {
867 return false;
868 }
869
870 @Override
871 public void commitAllTabClosures() {
872 }
873
874 @Override
875 public void commitTabClosure(int tabId) {
876 }
877
878 @Override
879 public void cancelTabClosure(int tabId) {
880 }
881
882 @Override
883 public TabList getComprehensiveModel() {
884 return this;
885 }
886
887 @Override
888 public void addObserver(TabModelObserver observer) {
889 mObservers.addObserver(observer);
890 }
891
892 @Override
893 public void removeObserver(TabModelObserver observer) {
894 mObservers.removeObserver(observer);
895 }
896
897 private static boolean isTabIdInEntryList(List<Entry> entries, int tabId) {
898 for (int i = 0; i < entries.size(); i++) {
899 if (entries.get(i).tabId == tabId) return true;
900 }
901 return false;
902 }
903 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698