Bug 1275662 - Close all tabs and clean up session files when "Open Tabs" is selected in "Clear private data on exit" or "Clear private data" pref. r=JanH,sebastian
authorNevin Chen <cnevinchen@gmail.com>
Thu, 29 Dec 2016 14:22:45 +0800
changeset 375258 8bfeb8f4a4f5f63b220e18ee817f90281ef4c242
parent 375257 6dad16396f745d39086c813734fc1fd6323e9725
child 375259 21e0b0c5284a9f2bad562a79764b0de32be7ec74
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersJanH, sebastian
bugs1275662
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1275662 - Close all tabs and clean up session files when "Open Tabs" is selected in "Clear private data on exit" or "Clear private data" pref. r=JanH,sebastian MozReview-Commit-ID: GoUQVDIzYbI
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/Tabs.java
mobile/android/base/java/org/mozilla/gecko/home/HomePager.java
mobile/android/chrome/content/browser.js
mobile/android/components/SessionStore.js
mobile/android/modules/Sanitizer.jsm
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -736,16 +736,17 @@ public class BrowserApp extends GeckoApp
             "Menu:Remove",
             "LightweightTheme:Update",
             "Tab:Added",
             "Video:Play",
             "CharEncoding:Data",
             "CharEncoding:State",
             "Settings:Show",
             "Updater:Launch",
+            "Sanitize:OpenTabs",
             null);
 
         EventDispatcher.getInstance().registerBackgroundThreadListener(this,
             "Experiments:GetActive",
             "Experiments:SetOverride",
             "Experiments:ClearOverride",
             "Favicon:Request",
             "Feedback:MaybeLater",
@@ -1449,16 +1450,17 @@ public class BrowserApp extends GeckoApp
             "Menu:Remove",
             "LightweightTheme:Update",
             "Tab:Added",
             "Video:Play",
             "CharEncoding:Data",
             "CharEncoding:State",
             "Settings:Show",
             "Updater:Launch",
+            "Sanitize:OpenTabs",
             null);
 
         EventDispatcher.getInstance().unregisterBackgroundThreadListener(this,
             "Experiments:GetActive",
             "Experiments:SetOverride",
             "Experiments:ClearOverride",
             "Favicon:Request",
             "Feedback:MaybeLater",
@@ -1909,16 +1911,21 @@ public class BrowserApp extends GeckoApp
                         .execute(IconsHelper.createBase64EventCallback(callback));
                 break;
 
             case "Feedback:MaybeLater":
                 SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
                 settings.edit().putInt(getPackageName() + ".feedback_launch_count", 0).apply();
                 break;
 
+            case "Sanitize:OpenTabs":
+                Tabs.getInstance().closeAll();
+                callback.sendSuccess(null);
+                break;
+
             case "Sanitize:ClearHistory":
                 BrowserDB.from(getProfile()).clearHistory(
                         getContentResolver(), message.getBoolean("clearSearchHistory", false));
                 callback.sendSuccess(null);
                 break;
 
             case "Sanitize:ClearSyncedTabs":
                 FennecTabsRepository.deleteNonLocalClientsAndTabs(getContext());
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -580,27 +580,30 @@ public abstract class GeckoApp
 
             final JSONObject res = new JSONObject();
             try {
                 res.put("sanitize", clearObj);
             } catch (JSONException ex) {
                 Log.e(LOGTAG, "Error adding sanitize object", ex);
             }
 
-            // If the user has opted out of session restore, and does want to clear history
+            // If the user wants to clear open tabs, or else has opted out of session restore and does want to clear history,
             // we also want to prevent the current session info from being saved.
-            if (clearObj.has("private.data.history")) {
-                final String sessionRestore = getSessionRestorePreference(getSharedPreferences());
-                try {
+            try {
+                if (clearObj.has("private.data.openTabs")) {
+                    res.put("dontSaveSession", true);
+                } else if (clearObj.has("private.data.history")) {
+
+                    final String sessionRestore = getSessionRestorePreference(getSharedPreferences());
                     res.put("dontSaveSession", "quit".equals(sessionRestore));
-                } catch (JSONException ex) {
-                    Log.e(LOGTAG, "Error adding session restore data", ex);
+
                 }
+            } catch (JSONException ex) {
+                Log.e(LOGTAG, "Error adding session restore data", ex);
             }
-
             GeckoAppShell.notifyObservers("Browser:Quit", res.toString());
             // We don't call doShutdown() here because this creates a race condition which can
             // cause the clearing of private data to fail. Instead, we shut down the UI only after
             // we're done sanitizing.
             return true;
         }
 
         return super.onOptionsItemSelected(item);
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -80,16 +80,22 @@ public class Tabs implements BundleEvent
     private volatile boolean mInitialTabsAdded;
 
     private Context mAppContext;
     private LayerView mLayerView;
     private ContentObserver mBookmarksContentObserver;
     private PersistTabsRunnable mPersistTabsRunnable;
     private int mPrivateClearColor;
 
+    public void closeAll() {
+        for (final Tab tab : mOrder) {
+            Tabs.getInstance().closeTab(tab, false);
+        }
+    }
+
     private static class PersistTabsRunnable implements Runnable {
         private final BrowserDB db;
         private final Context context;
         private final Iterable<Tab> tabs;
 
         public PersistTabsRunnable(final Context context, Iterable<Tab> tabsInOrder) {
             this.context = context;
             this.db = BrowserDB.from(context);
--- a/mobile/android/base/java/org/mozilla/gecko/home/HomePager.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/HomePager.java
@@ -220,18 +220,23 @@ public class HomePager extends ViewPager
         adapter.setPanelStateChangeListener(mPanelStateChangeListener);
         adapter.setCanLoadHint(true);
         setAdapter(adapter);
 
         // Don't show the tabs strip until we have the
         // list of panels in place.
         mTabStrip.setVisibility(View.INVISIBLE);
 
-        // Load list of panels from configuration
-        lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks);
+        // If HomeConfigLoader already exist, force load to select the current item
+        if (lm.getLoader(LOADER_ID_CONFIG) != null) {
+            lm.getLoader(LOADER_ID_CONFIG).forceLoad();
+        } else {
+            // Load list of panels from configuration
+            lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks);
+        }
 
         if (shouldAnimate) {
             animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
                 @Override
                 public void onPropertyAnimationStart() {
                     setLayerType(View.LAYER_TYPE_HARDWARE, null);
                 }
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1465,16 +1465,22 @@ var BrowserApp = {
 
       key = key.replace("private.data.", "");
 
       switch (key) {
         case "cookies_sessions":
           promises.push(Sanitizer.clearItem("cookies"));
           promises.push(Sanitizer.clearItem("sessions"));
           break;
+        case "openTabs":
+          if (aShutdown === true) {
+            Services.obs.notifyObservers(null, "browser:purge-session-tabs", "");
+            break;
+          }
+          // fall-through if aShutdown is false
         default:
           promises.push(Sanitizer.clearItem(key));
       }
     }
 
     Promise.all(promises).then(function() {
       GlobalEventDispatcher.sendRequest({
         type: "Sanitize:Finished",
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -175,16 +175,17 @@ SessionStore.prototype = {
     let self = this;
     let observerService = Services.obs;
     switch (aTopic) {
       case "app-startup":
         observerService.addObserver(this, "final-ui-startup", true);
         observerService.addObserver(this, "domwindowopened", true);
         observerService.addObserver(this, "domwindowclosed", true);
         observerService.addObserver(this, "browser:purge-session-history", true);
+        observerService.addObserver(this, "browser:purge-session-tabs", true);
         observerService.addObserver(this, "quit-application-requested", true);
         observerService.addObserver(this, "quit-application-proceeding", true);
         observerService.addObserver(this, "quit-application", true);
         observerService.addObserver(this, "Session:Restore", true);
         observerService.addObserver(this, "Session:NotifyLocationChange", true);
         observerService.addObserver(this, "Tab:KeepZombified", true);
         observerService.addObserver(this, "application-background", true);
         observerService.addObserver(this, "application-foreground", true);
@@ -231,18 +232,19 @@ SessionStore.prototype = {
         observerService.removeObserver(this, "quit-application-proceeding");
         observerService.removeObserver(this, "quit-application");
 
         // Flush all pending writes to disk now
         this.flushPendingState();
         this._loadState = STATE_QUITTING_FLUSHED;
 
         break;
+      case "browser:purge-session-tabs":
       case "browser:purge-session-history": // catch sanitization
-        log("browser:purge-session-history");
+        log(aTopic);
         this._clearDisk();
 
         // Clear all data about closed tabs
         this._forgetClosedTabs();
 
         if (this._loadState == STATE_RUNNING) {
           // Save the purged state immediately
           this.saveState();
@@ -1775,16 +1777,22 @@ SessionStore.prototype = {
       this._lastClosedTabIndex = -1;
     }
     if (this._notifyClosedTabs) {
       this._sendClosedTabsToJava(aWindow);
     }
   },
 
   _sendClosedTabsToJava: function ss_sendClosedTabsToJava(aWindow) {
+
+    // If the app is shutting down, we don't need to do anything.
+    if (this._loadState <= STATE_QUITTING) {
+      return;
+    }
+
     if (!aWindow.__SSID) {
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
     }
 
     let closedTabs = this._windows[aWindow.__SSID].closedTabs;
     let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(aWindow.BrowserApp.selectedBrowser);
 
     let tabs = closedTabs
--- a/mobile/android/modules/Sanitizer.jsm
+++ b/mobile/android/modules/Sanitizer.jsm
@@ -163,16 +163,36 @@ Sanitizer.prototype = {
       get canClear()
       {
         // bug 347231: Always allow clearing history due to dependencies on
         // the browser:purge-session-history notification. (like error console)
         return true;
       }
     },
 
+    openTabs: {
+      clear: function ()
+      {
+        return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:OpenTabs" })
+          .catch(e => Cu.reportError("Java-side tab clearing failed: " + e))
+          .then(function() {
+            try {
+              // clear "Recently Closed" tabs in Android App
+              Services.obs.notifyObservers(null, "browser:purge-session-tabs", "");
+            }
+            catch (e) { }
+          });
+      },
+
+      get canClear()
+      {
+        return true;
+      }
+    },
+
     searchHistory: {
       clear: function ()
       {
         return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearHistory", clearSearchHistory: true })
           .catch(e => Cu.reportError("Java-side search history clearing failed: " + e))
       },
 
       get canClear()