Bug 1501108 - [3.4] Add a StorageController API to delete session context data. r=baku,snorp,geckoview-reviewers
☠☠ backed out by 095d253f97be ☠ ☠
authorEugen Sawin <esawin@me73.com>
Mon, 15 Apr 2019 20:59:24 +0000
changeset 469570 1936dde5f34c
parent 469569 de36c9fb8c65
child 469571 9bf188c86f9c
push id35874
push userccoroiu@mozilla.com
push dateTue, 16 Apr 2019 04:04:58 +0000
treeherdermozilla-central@be3f40425b52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, snorp, geckoview-reviewers
bugs1501108
milestone68.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 1501108 - [3.4] Add a StorageController API to delete session context data. r=baku,snorp,geckoview-reviewers Differential Revision: https://phabricator.services.mozilla.com/D23732
mobile/android/components/geckoview/GeckoViewStartup.js
mobile/android/geckoview/api.txt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
--- a/mobile/android/components/geckoview/GeckoViewStartup.js
+++ b/mobile/android/components/geckoview/GeckoViewStartup.js
@@ -126,29 +126,41 @@ GeckoViewStartup.prototype = {
 
         ChromeUtils.import("resource://gre/modules/NotificationDB.jsm");
 
         // Initialize safe browsing module. This is required for content
         // blocking features and manages blocklist downloads and updates.
         SafeBrowsing.init();
 
         // Listen for global EventDispatcher messages
-        EventDispatcher.instance.registerListener(this,
-          ["GeckoView:ResetUserPrefs",
-           "GeckoView:SetDefaultPrefs",
-           "GeckoView:SetLocale"]);
+        EventDispatcher.instance.registerListener(this, [
+          "GeckoView:ClearSessionContextData",
+          "GeckoView:ResetUserPrefs",
+          "GeckoView:SetDefaultPrefs",
+          "GeckoView:SetLocale",
+        ]);
         break;
       }
     }
   },
 
   onEvent(aEvent, aData, aCallback) {
     debug `onEvent ${aEvent}`;
 
     switch (aEvent) {
+      case "GeckoView:ClearSessionContextData": {
+        let pattern = {};
+        if (aData.contextId !== null) {
+          pattern = { geckoViewSessionContextId: aData.contextId };
+        }
+        Services.clearData.deleteDataFromOriginAttributesPattern(pattern);
+        Services.qms.clearStoragesForOriginAttributesPattern(
+          JSON.stringify(pattern));
+        break;
+      }
       case "GeckoView:ResetUserPrefs": {
         const prefs = new Preferences();
         prefs.reset(aData.names);
         break;
       }
       case "GeckoView:SetDefaultPrefs": {
         const prefs = new Preferences({ defaultBranch: true });
         for (const name of Object.keys(aData)) {
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -73,16 +73,17 @@ import org.mozilla.geckoview.MediaElemen
 import org.mozilla.geckoview.OverscrollEdgeEffect;
 import org.mozilla.geckoview.PanZoomController;
 import org.mozilla.geckoview.RuntimeSettings;
 import org.mozilla.geckoview.RuntimeTelemetry;
 import org.mozilla.geckoview.ScreenLength;
 import org.mozilla.geckoview.SessionAccessibility;
 import org.mozilla.geckoview.SessionFinder;
 import org.mozilla.geckoview.SessionTextInput;
+import org.mozilla.geckoview.StorageController;
 import org.mozilla.geckoview.WebExtension;
 import org.mozilla.geckoview.WebMessage;
 import org.mozilla.geckoview.WebRequest;
 import org.mozilla.geckoview.WebRequestError;
 import org.mozilla.geckoview.WebResponse;
 
 package org.mozilla.geckoview {
 
@@ -261,25 +262,25 @@ package org.mozilla.geckoview {
     method @AnyThread @Nullable public GeckoResult<U> onValue(@Nullable T);
   }
 
   public static final class GeckoResult.UncaughtException extends RuntimeException {
     ctor public UncaughtException(Throwable);
   }
 
   public final class GeckoRuntime implements Parcelable {
-    ctor public GeckoRuntime();
     method @UiThread public void attachTo(@NonNull Context);
     method @UiThread public void configurationChanged(@NonNull Configuration);
     method @UiThread @NonNull public static GeckoRuntime create(@NonNull Context);
     method @UiThread @NonNull public static GeckoRuntime create(@NonNull Context, @NonNull GeckoRuntimeSettings);
     method @UiThread @NonNull public static synchronized GeckoRuntime getDefault(@NonNull Context);
     method @UiThread @Nullable public GeckoRuntime.Delegate getDelegate();
     method @UiThread @Nullable public File getProfileDir();
     method @AnyThread @NonNull public GeckoRuntimeSettings getSettings();
+    method @AnyThread @NonNull public StorageController getStorageController();
     method @UiThread @NonNull public RuntimeTelemetry getTelemetry();
     method @UiThread public void orientationChanged();
     method @UiThread public void orientationChanged(int);
     method @AnyThread public void readFromParcel(@NonNull Parcel);
     method @UiThread @NonNull public GeckoResult<Void> registerWebExtension(@NonNull WebExtension);
     method @UiThread public void setDelegate(@Nullable GeckoRuntime.Delegate);
     method @AnyThread public void shutdown();
     field public static final String ACTION_CRASHED = "org.mozilla.gecko.ACTION_CRASHED";
@@ -777,16 +778,17 @@ package org.mozilla.geckoview {
     field @NonNull public final String uri;
   }
 
   @AnyThread public final class GeckoSessionSettings implements Parcelable {
     ctor public GeckoSessionSettings();
     ctor public GeckoSessionSettings(@NonNull GeckoSessionSettings);
     method public boolean getAllowJavascript();
     method @Nullable public String getChromeUri();
+    method @Nullable public String getContextId();
     method public int getDisplayMode();
     method public boolean getFullAccessibilityTree();
     method public int getScreenId();
     method public boolean getSuspendMediaWhenInactive();
     method public boolean getUseMultiprocess();
     method public boolean getUsePrivateMode();
     method public boolean getUseTrackingProtection();
     method public int getUserAgentMode();
@@ -814,16 +816,17 @@ package org.mozilla.geckoview {
   }
 
   @AnyThread public static final class GeckoSessionSettings.Builder {
     ctor public Builder();
     ctor public Builder(GeckoSessionSettings);
     method @NonNull public GeckoSessionSettings.Builder allowJavascript(boolean);
     method @NonNull public GeckoSessionSettings build();
     method @NonNull public GeckoSessionSettings.Builder chromeUri(@NonNull String);
+    method @NonNull public GeckoSessionSettings.Builder contextId(@Nullable String);
     method @NonNull public GeckoSessionSettings.Builder displayMode(int);
     method @NonNull public GeckoSessionSettings.Builder fullAccessibilityTree(boolean);
     method @NonNull public GeckoSessionSettings.Builder screenId(int);
     method @NonNull public GeckoSessionSettings.Builder suspendMediaWhenInactive(boolean);
     method @NonNull public GeckoSessionSettings.Builder useMultiprocess(boolean);
     method @NonNull public GeckoSessionSettings.Builder usePrivateMode(boolean);
     method @NonNull public GeckoSessionSettings.Builder useTrackingProtection(boolean);
     method @NonNull public GeckoSessionSettings.Builder userAgentMode(int);
@@ -1024,16 +1027,22 @@ package org.mozilla.geckoview {
     method @UiThread public boolean onKeyMultiple(int, int, @NonNull KeyEvent);
     method @UiThread public boolean onKeyPreIme(int, @NonNull KeyEvent);
     method @UiThread public boolean onKeyUp(int, @NonNull KeyEvent);
     method @UiThread public void onProvideAutofillVirtualStructure(@NonNull ViewStructure, int);
     method @UiThread public void setDelegate(@Nullable GeckoSession.TextInputDelegate);
     method @UiThread public synchronized void setView(@Nullable View);
   }
 
+  public final class StorageController {
+    ctor public StorageController();
+    method @AnyThread public void clearAllSessionContextData();
+    method @AnyThread public void clearSessionContextData(@NonNull String);
+  }
+
   public class WebExtension {
     ctor public WebExtension(@NonNull String, @NonNull String);
     ctor public WebExtension(@NonNull String);
     field @NonNull public final String id;
     field @NonNull public final String location;
   }
 
   @AnyThread public abstract class WebMessage {
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
@@ -1237,9 +1237,78 @@ class NavigationDelegateTest : BaseSessi
             @AssertCalled(count = 1)
             override fun onTitleChange(session: GeckoSession, title: String?) {
                 assertThat("Title should not be empty", title, not(isEmptyOrNullString()))
                 assertThat("Title should match", title,
                            equalTo("storage=ctx1"))
             }
         })
     }
+
+    @WithDevToolsAPI
+    @Test fun clearSessionContextData() {
+        val session1 = sessionRule.createOpenSession(
+                GeckoSessionSettings.Builder(mainSession.settings)
+                .contextId("1")
+                .build())
+        session1.loadTestPath(STORAGE_TITLE_HTML_PATH + "?ctx1")
+        session1.waitForPageStop()
+
+        session1.forCallbacksDuringWait(object: Callbacks.ContentDelegate {
+            @AssertCalled(count = 1)
+            override fun onTitleChange(session: GeckoSession, title: String?) {
+                assertThat("Title should not be empty", title, not(isEmptyOrNullString()))
+                assertThat("Title should match", title,
+                           equalTo("storage=ctx1"))
+            }
+        })
+
+        session1.loadTestPath(STORAGE_TITLE_HTML_PATH)
+        session1.waitForPageStop()
+
+        session1.forCallbacksDuringWait(object: Callbacks.ContentDelegate {
+            @AssertCalled(count = 1)
+            override fun onTitleChange(session: GeckoSession, title: String?) {
+                assertThat("Title should not be empty", title, not(isEmptyOrNullString()))
+                assertThat("Title should match", title,
+                           equalTo("storage=ctx1"))
+            }
+        })
+
+        session1.close()
+
+        val session2 = sessionRule.createOpenSession(
+                GeckoSessionSettings.Builder(mainSession.settings)
+                .contextId("1")
+                .build())
+        session2.loadTestPath(STORAGE_TITLE_HTML_PATH)
+        session2.waitForPageStop()
+
+        session2.forCallbacksDuringWait(object: Callbacks.ContentDelegate {
+            @AssertCalled(count = 1)
+            override fun onTitleChange(session: GeckoSession, title: String?) {
+                assertThat("Title should not be empty", title, not(isEmptyOrNullString()))
+                assertThat("Title should match", title,
+                           equalTo("storage=ctx1"))
+            }
+        })
+
+        session2.close()
+
+        sessionRule.runtime.storageController.clearSessionContextData("1")
+
+        val session3 = sessionRule.createOpenSession(
+                GeckoSessionSettings.Builder(mainSession.settings)
+                .contextId("1")
+                .build())
+        session3.loadTestPath(STORAGE_TITLE_HTML_PATH)
+        session3.waitForPageStop()
+
+        session3.forCallbacksDuringWait(object: Callbacks.ContentDelegate {
+            @AssertCalled(count = 1)
+            override fun onTitleChange(session: GeckoSession, title: String?) {
+                assertThat("Title should not be empty", title, not(isEmptyOrNullString()))
+                assertThat("Title should match", title,
+                           equalTo("storage=null"))
+            }
+        })
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
@@ -82,16 +82,22 @@ public final class GeckoRuntime implemen
      * This is a key for extra data sent with {@link #ACTION_CRASHED}. The value is
      * a boolean indicating whether or not the crash was fatal or not. If true, the
      * main application process was affected by the crash. If false, only an internal
      * process used by Gecko has crashed and the application may be able to recover.
      * @see GeckoSession.ContentDelegate#onCrash(GeckoSession)
      */
     public static final String EXTRA_CRASH_FATAL = "fatal";
 
+    private final StorageController mStorageController;
+
+    private GeckoRuntime() {
+        mStorageController = new StorageController();
+    }
+
     private static GeckoRuntime sDefaultRuntime;
 
     /**
      * Get the default runtime for the given context.
      * This will create and initialize the runtime with the default settings.
      *
      * Note: Only use this for session-less apps.
      *       For regular apps, use create() instead.
@@ -447,16 +453,27 @@ public final class GeckoRuntime implemen
      *                       {@link android.content.res.Configuration}.
      */
     @UiThread
     public void orientationChanged(final int newOrientation) {
         ThreadUtils.assertOnUiThread();
         GeckoScreenOrientation.getInstance().update(newOrientation);
     }
 
+
+    /**
+     * Get the storage controller for this runtime.
+     *
+     * @return The {@link StorageController} for this instance.
+     */
+    @AnyThread
+    public @NonNull StorageController getStorageController() {
+        return mStorageController;
+    }
+
     @Override // Parcelable
     @AnyThread
     public int describeContents() {
         return 0;
     }
 
     @Override // Parcelable
     @AnyThread
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
@@ -81,16 +81,20 @@ public final class GeckoSessionSettings 
 
         /**
          * Set the session context ID for this instance.
          * Setting a context ID partitions the cookie jars based on the provided
          * IDs. This isolates the browser storage like cookies and localStorage
          * between sessions, only sessions that share the same ID share storage
          * data.
          *
+         * Warning: Storage data is collected persistently for each context,
+         * to delete context data, call {@link StorageController#clearSessionContextData}
+         * for the given context.
+         *
          * @param value The custom context ID.
          *              The default ID is null, which removes isolation for this
          *              instance.
          */
         public @NonNull Builder contextId(final @Nullable String value) {
             mSettings.setContextId(value);
             return this;
         }
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java
@@ -0,0 +1,56 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * vim: ts=4 sw=4 expandtab:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.geckoview;
+
+import android.support.annotation.AnyThread;
+import android.support.annotation.NonNull;
+
+import org.mozilla.gecko.EventDispatcher;
+import org.mozilla.gecko.util.GeckoBundle;
+
+/**
+ * Manage and control runtime storage data.
+ *
+ * Retrieve an instance via {@link GeckoRuntime#getStorageController}.
+ */
+public final class StorageController {
+    /**
+     * Clear all browser storage data like cookies and localStorage for the
+     * given context.
+     *
+     * Note: Any open session may re-accumulate previously cleared data. To
+     * ensure that no persistent data is left behind, you need to close all
+     * sessions for the given context prior to clearing data.
+     *
+     * @param contextId The context ID for the storage data to be deleted.
+     *                  For null, all storage data will be cleared.
+     */
+    @AnyThread
+    public void clearSessionContextData(final @NonNull String contextId) {
+        final GeckoBundle bundle = new GeckoBundle(1);
+        bundle.putString("contextId", contextId);
+
+        EventDispatcher.getInstance().dispatch(
+            "GeckoView:ClearSessionContextData", bundle);
+    }
+
+    /**
+     * Clear all browser storage data like cookies and localStorage.
+     *
+     * Note: Any open session may re-accumulate previously cleared data. To
+     * ensure that no persistent data is left behind, you need to close all
+     * sessions prior to clearing data.
+     */
+    @AnyThread
+    public void clearAllSessionContextData() {
+        final GeckoBundle bundle = new GeckoBundle(1);
+        bundle.putString("contextId", null);
+
+        EventDispatcher.getInstance().dispatch(
+            "GeckoView:ClearSessionContextData", bundle);
+    }
+}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
@@ -58,16 +58,24 @@ exclude: true
   [`HistoryDelegate`][68.12] and added `gotoHistoryIndex` to [`GeckoSession`][68.13].
 
 [68.12]: ../GeckoSession.HistoryDelegate.html
 [68.13]: ../GeckoSession.html
 
 - [`GeckoView`][65.5] will not create a [`GeckoSession`][65.9] anymore when
   attached to a window without a session.
 
+- Added API for session context assignment
+  [`GeckoSessionSettings.Builder.contextId`][68.14] and deletion of data
+  related to a session context
+  [`StorageController.clearSessionContextData`][68.15].
+
+[68.14]: ../GeckoSessionSettings.Builder.html#contextId-
+[68.15]: ../StorageController.html#clearSessionContextData-java.lang.String-
+
 ## v67
 - Added [`setAutomaticFontSizeAdjustment`][67.2] to
   [`GeckoRuntimeSettings`][67.3] for automatically adjusting font size settings
   depending on the OS-level font size setting.
 
 [67.2]: ../GeckoRuntimeSettings.html#setAutomaticFontSizeAdjustment-boolean-
 [67.3]: ../GeckoRuntimeSettings.html
 
@@ -264,9 +272,9 @@ exclude: true
 [65.23]: ../GeckoSession.FinderResult.html
 
 - Update [`CrashReporter#sendCrashReport`][65.24] to return the crash ID as a
   [`GeckoResult<String>`][65.25].
 
 [65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
 [65.25]: ../GeckoResult.html
 
-[api-version]: 7cba6fdca8edb1858d73704a06dad936f78c9f64
+[api-version]: fa2863734daac0ec5cb0671b030139de3aac5029