Bug 1190627 - Part 1 - Reorganise session store file names. r=sebastian draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Sun, 29 May 2016 13:25:43 +0200
changeset 401231 1409fb229ef851d0d0a23552427ae67803c70c7b
parent 401230 03c68e7d4e3557fc4c92a580a7ef1d883a759cc2
child 401232 a3ebc48970c2cd62da4c8dd34600491f58a81cb8
push id26408
push usermozilla@buttercookie.de
push dateTue, 16 Aug 2016 18:08:19 +0000
reviewerssebastian
bugs1190627
milestone51.0a1
Bug 1190627 - Part 1 - Reorganise session store file names. r=sebastian Currently, despite its name sessionstore.bak isn't actually a backup copy, but simply contains the last session if we aren't restoring tabs automatically and is used for powering what used to be the "Tabs from last time" section of the Recent Tabs panel. This patch changes its filename to sessionstore.old, which frees up sessionstore.bak to be used for an actual backup copy of the current session store data. If we are not restoring tabs automatically, sessionstore.old will be freshly recreated during each app startup by copying from sessionstore.js's contents, whereas if we *are* restoring automatically, any sessionstore.old file older than a day will be expired anyway, therefore no special migration logic is necessary for this change. MozReview-Commit-ID: H7Gl5MQi2J4
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/home/RecentTabsAdapter.java
mobile/android/components/SessionStore.js
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1409,17 +1409,17 @@ public abstract class GeckoApp
                     GeckoApp.this.notifyAll();
                 }
 
                 // If we are doing a restore, send the parsed session data to Gecko.
                 if (!mIsRestoringActivity) {
                     GeckoAppShell.notifyObservers("Session:Restore", restoreMessage);
                 }
 
-                // Make sure sessionstore.bak is either updated or deleted as necessary.
+                // Make sure sessionstore.old is either updated or deleted as necessary.
                 getProfile().updateSessionFile(mShouldRestore);
             }
         });
 
         // Perform background initialization.
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
--- a/mobile/android/base/java/org/mozilla/gecko/home/RecentTabsAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/RecentTabsAdapter.java
@@ -142,20 +142,20 @@ public class RecentTabsAdapter extends R
     }
 
     private void readPreviousSessionData() {
         // If we happen to initialise before GeckoApp, waiting on either the main or the background
         // thread can lead to a deadlock, so we have to run on a separate thread instead.
         final Thread parseThread = new Thread(new Runnable() {
             @Override
             public void run() {
-                // Make sure that the start up code has had a chance to update sessionstore.bak as necessary.
+                // Make sure that the start up code has had a chance to update sessionstore.old as necessary.
                 GeckoProfile.get(context).waitForOldSessionDataProcessing();
 
-                final String jsonString = GeckoProfile.get(context).readSessionFile(true);
+                final String jsonString = GeckoProfile.get(context).readPreviousSessionFile();
                 if (jsonString == null) {
                     // No previous session data.
                     return;
                 }
 
                 final List<ClosedTab> parsedTabs = new ArrayList<>();
 
                 new SessionParser() {
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -80,20 +80,22 @@ SessionStore.prototype = {
   _notifyClosedTabs: false,
 
   init: function ss_init() {
     loggingEnabled = Services.prefs.getBoolPref("browser.sessionstore.debug_logging");
 
     // Get file references
     this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
     this._sessionFileBackup = this._sessionFile.clone();
+    this._sessionFilePrevious = this._sessionFile.clone();
     this._sessionFileTemp = this._sessionFile.clone();
-    this._sessionFile.append("sessionstore.js");
-    this._sessionFileBackup.append("sessionstore.bak");
-    this._sessionFileTemp.append(this._sessionFile.leafName + ".tmp");
+    this._sessionFile.append("sessionstore.js"); // The main session store save file.
+    this._sessionFileBackup.append("sessionstore.bak"); // A backup copy to guard against interrupted writes.
+    this._sessionFilePrevious.append("sessionstore.old"); // The previous session's file, used for what used to be the "Tabs from last time".
+    this._sessionFileTemp.append(this._sessionFile.leafName + ".tmp"); // Temporary file for writing changes to disk.
 
     this._loadState = STATE_STOPPED;
     this._startupRestoreFinished = false;
 
     this._interval = Services.prefs.getIntPref("browser.sessionstore.interval");
     this._maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
 
     // Copy changes in Gecko settings to their Java counterparts,
@@ -106,16 +108,17 @@ SessionStore.prototype = {
       SharedPreferences.forApp().setIntPref(PREFS_MAX_CRASH_RESUMES,
         Services.prefs.getIntPref(PREFS_MAX_CRASH_RESUMES));
     }, false);
   },
 
   _clearDisk: function ss_clearDisk() {
     OS.File.remove(this._sessionFile.path);
     OS.File.remove(this._sessionFileBackup.path);
+    OS.File.remove(this._sessionFilePrevious.path);
     OS.File.remove(this._sessionFileTemp.path);
   },
 
   observe: function ss_observe(aSubject, aTopic, aData) {
     let self = this;
     let observerService = Services.obs;
     switch (aTopic) {
       case "app-startup":
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java
@@ -70,17 +70,18 @@ public final class GeckoProfile {
     public static final String DEFAULT_PROFILE = "default";
     // Profile is using a custom directory outside of the Mozilla directory.
     public static final String CUSTOM_PROFILE = "";
     public static final String GUEST_PROFILE_DIR = "guest";
 
     // Session store
     private static final String SESSION_FILE = "sessionstore.js";
     private static final String SESSION_FILE_BACKUP = "sessionstore.bak";
-    private static final long MAX_BACKUP_FILE_AGE = 1000 * 3600 * 24; // 24 hours
+    private static final String SESSION_FILE_PREVIOUS = "sessionstore.old";
+    private static final long MAX_PREVIOUS_FILE_AGE = 1000 * 3600 * 24; // 24 hours
     private static final int SESSION_STORE_EMPTY_JSON_LENGTH = 14; // length of {"windows":[]}
 
     private boolean mOldSessionDataProcessed = false;
 
     private static final ConcurrentHashMap<String, GeckoProfile> sProfileCache =
             new ConcurrentHashMap<String, GeckoProfile>(
                     /* capacity */ 4, /* load factor */ 0.75f, /* concurrency */ 2);
     private static String sDefaultProfileName;
@@ -584,36 +585,36 @@ public final class GeckoProfile {
         }
         Log.d(LOGTAG, "Attempting to write new profile creation date");
         writeFile(TIMES_PATH, obj.toString()); // Ideally we'd throw here too.
     }
 
     /**
      * Updates the state of the old session data file.
      *
-     * sessionstore.js should hold the current session, and sessionstore.bak should
+     * sessionstore.js should hold the current session, and sessionstore.old should
      * hold the previous session (where it is used to read the "tabs from last time").
      * If we're not restoring tabs automatically, sessionstore.js needs to be moved to
-     * sessionstore.bak, so we can display the correct "tabs from last time".
-     * If we *are* restoring tabs, we need to delete outdated copies of sessionstore.bak,
+     * sessionstore.old, so we can display the correct "tabs from last time".
+     * If we *are* restoring tabs, we need to delete outdated copies of sessionstore.old,
      * so we don't continue showing stale "tabs from last time" indefinitely.
      *
      * @param shouldRestore Pass true if we are automatically restoring last session's tabs.
      */
     public void updateSessionFile(boolean shouldRestore) {
-        File sessionFileBackup = getFile(SESSION_FILE_BACKUP);
+        File sessionFilePrevious = getFile(SESSION_FILE_PREVIOUS);
         if (!shouldRestore) {
             File sessionFile = getFile(SESSION_FILE);
             if (sessionFile != null && sessionFile.exists()) {
-                sessionFile.renameTo(sessionFileBackup);
+                sessionFile.renameTo(sessionFilePrevious);
             }
         } else {
-            if (sessionFileBackup != null && sessionFileBackup.exists() &&
-                    System.currentTimeMillis() - sessionFileBackup.lastModified() > MAX_BACKUP_FILE_AGE) {
-                sessionFileBackup.delete();
+            if (sessionFilePrevious != null && sessionFilePrevious.exists() &&
+                    System.currentTimeMillis() - sessionFilePrevious.lastModified() > MAX_PREVIOUS_FILE_AGE) {
+                sessionFilePrevious.delete();
             }
         }
         synchronized (this) {
             mOldSessionDataProcessed = true;
             notifyAll();
         }
     }
 
@@ -629,25 +630,41 @@ public final class GeckoProfile {
         }
     }
 
     /**
      * Get the string from a session file.
      *
      * The session can either be read from sessionstore.js or sessionstore.bak.
      * In general, sessionstore.js holds the current session, and
-     * sessionstore.bak holds the previous session.
+     * sessionstore.bak holds a backup copy in case of interrupted writes.
      *
      * @param readBackup if true, the session is read from sessionstore.bak;
      *                   otherwise, the session is read from sessionstore.js
      *
      * @return the session string
      */
     public String readSessionFile(boolean readBackup) {
-        File sessionFile = getFile(readBackup ? SESSION_FILE_BACKUP : SESSION_FILE);
+        return readSessionFile(readBackup ? SESSION_FILE_BACKUP : SESSION_FILE);
+    }
+
+    /**
+     * Get the string from last session's session file.
+     *
+     * If we are not restoring tabs automatically, sessionstore.old will contain
+     * the previous session.
+     *
+     * @return the session string
+     */
+    public String readPreviousSessionFile() {
+        return readSessionFile(SESSION_FILE_PREVIOUS);
+    }
+
+    private String readSessionFile(String fileName) {
+        File sessionFile = getFile(fileName);
 
         try {
             if (sessionFile != null && sessionFile.exists()) {
                 return readFile(sessionFile);
             }
         } catch (IOException ioe) {
             Log.e(LOGTAG, "Unable to read session file", ioe);
         }