Bug 1489669 - [1.4] Add Storage Controller API. r=baku,snorp a=jcristau
authorEugen Sawin <esawin@me73.com>
Wed, 22 May 2019 20:21:09 +0000
changeset 536453 d6c54c50418ca7f3b4714a13a97455540e963785
parent 536452 3b40f41fc5bef54def5ea1d0ccc0727f142dfdbd
child 536454 b7c117554a7ea876c00b404f0d5985cf2af67fd1
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, snorp, jcristau
bugs1489669
milestone68.0
Bug 1489669 - [1.4] Add Storage Controller API. r=baku,snorp a=jcristau Differential Revision: https://phabricator.services.mozilla.com/D32153
mobile/android/components/geckoview/GeckoViewStartup.js
mobile/android/geckoview/api.txt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.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
mobile/android/modules/geckoview/GeckoViewStorageController.jsm
mobile/android/modules/geckoview/moz.build
--- a/mobile/android/components/geckoview/GeckoViewStartup.js
+++ b/mobile/android/components/geckoview/GeckoViewStartup.js
@@ -53,16 +53,24 @@ GeckoViewStartup.prototype = {
           ged: [
             "GeckoView:RegisterWebExtension",
             "GeckoView:UnregisterWebExtension",
             "GeckoView:WebExtension:PortDisconnect",
             "GeckoView:WebExtension:PortMessageFromApp",
           ],
         });
 
+        GeckoViewUtils.addLazyGetter(this, "GeckoViewStorageController", {
+          module: "resource://gre/modules/GeckoViewStorageController.jsm",
+          ged: [
+            "GeckoView:ClearData",
+            "GeckoView:ClearHostData",
+          ],
+        });
+
         GeckoViewUtils.addLazyPrefObserver({
           name: "geckoview.console.enabled",
           default: false,
         }, {
           handler: _ => this.GeckoViewConsole,
         });
 
         // Handle invalid form submission. If we don't hook up to this,
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -72,16 +72,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 {
 
@@ -270,16 +271,17 @@ package org.mozilla.geckoview {
     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 @UiThread @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();
     method @UiThread @NonNull public GeckoResult<Void> unregisterWebExtension(@NonNull WebExtension);
@@ -1007,16 +1009,36 @@ 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 @NonNull public GeckoResult<Void> clearData(long);
+    method @AnyThread @NonNull public GeckoResult<Void> clearDataFromHost(@NonNull String, long);
+  }
+
+  public static class StorageController.ClearFlags {
+    ctor public ClearFlags();
+    field public static final long ALL = 512L;
+    field public static final long ALL_CACHES = 6L;
+    field public static final long AUTH_SESSIONS = 32L;
+    field public static final long COOKIES = 1L;
+    field public static final long DOM_STORAGES = 16L;
+    field public static final long IMAGE_CACHE = 4L;
+    field public static final long NETWORK_CACHE = 2L;
+    field public static final long PERMISSIONS = 64L;
+    field public static final long SITE_DATA = 471L;
+    field public static final long SITE_SETTINGS = 192L;
+  }
+
   public class WebExtension {
     ctor public WebExtension(@NonNull String, @NonNull String, long);
     ctor public WebExtension(@NonNull String);
     method @UiThread public void setMessageDelegate(@Nullable WebExtension.MessageDelegate, @NonNull String);
     field public final long flags;
     field @NonNull public final String id;
     field @NonNull public final String location;
   }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
@@ -155,16 +155,17 @@ public final class GeckoRuntime implemen
 
         return sDefaultRuntime;
     }
 
     private GeckoRuntimeSettings mSettings;
     private Delegate mDelegate;
     private RuntimeTelemetry mTelemetry;
     private WebExtensionEventDispatcher mWebExtensionDispatcher;
+    private StorageController mStorageController;
 
     /**
      * Attach the runtime to the given context.
      *
      * @param context The new context to attach to.
      */
     @UiThread
     public void attachTo(final @NonNull Context context) {
@@ -558,16 +559,34 @@ 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.
+     * The storage controller can be used to manage persistent storage data
+     * accumulated by {@link GeckoSession}.
+     *
+     * @return The {@link StorageController} for this instance.
+     */
+    @UiThread
+    public @NonNull StorageController getStorageController() {
+        ThreadUtils.assertOnUiThread();
+
+        if (mStorageController == null) {
+            mStorageController = new StorageController();
+        }
+        return mStorageController;
+    }
+
     @Override // Parcelable
     @AnyThread
     public int describeContents() {
         return 0;
     }
 
     @Override // Parcelable
     @AnyThread
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java
@@ -0,0 +1,163 @@
+/* -*- 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 java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import android.support.annotation.AnyThread;
+import android.support.annotation.LongDef;
+import android.support.annotation.NonNull;
+
+import org.mozilla.gecko.EventDispatcher;
+import org.mozilla.gecko.util.GeckoBundle;
+
+/**
+ * Manage runtime storage data.
+ *
+ * Retrieve an instance via {@link GeckoRuntime#getStorageController}.
+ */
+public final class StorageController {
+
+    // Keep in sync with GeckoViewStorageController.ClearFlags.
+    /**
+     * Flags used for data clearing operations.
+     */
+    public static class ClearFlags {
+        /**
+         * Cookies.
+         */
+        public static final long COOKIES = 1 << 0;
+
+        /**
+         * Network cache.
+         */
+        public static final long NETWORK_CACHE = 1 << 1;
+
+        /**
+         * Image cache.
+         */
+        public static final long IMAGE_CACHE = 1 << 2;
+
+        /**
+         * DOM storages.
+         */
+        public static final long DOM_STORAGES = 1 << 4;
+
+        /**
+         * Auth tokens and caches.
+         */
+        public static final long AUTH_SESSIONS = 1 << 5;
+
+        /**
+         * Site permissions.
+         */
+        public static final long PERMISSIONS = 1 << 6;
+
+        /**
+         * All caches.
+         */
+        public static final long ALL_CACHES = NETWORK_CACHE | IMAGE_CACHE;
+
+        /**
+         * All site settings (permissions, content preferences, security
+         * settings, etc.).
+         */
+        public static final long SITE_SETTINGS = 1 << 7 | PERMISSIONS;
+
+        /**
+         * All site-related data (cookies, storages, caches, permissions, etc.).
+         */
+        public static final long SITE_DATA =
+            1 << 8 | COOKIES | DOM_STORAGES | ALL_CACHES |
+            PERMISSIONS | SITE_SETTINGS;
+
+        /**
+         * All data.
+         */
+        public static final long ALL = 1 << 9;
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @LongDef(flag = true,
+             value = { ClearFlags.COOKIES,
+                       ClearFlags.NETWORK_CACHE,
+                       ClearFlags.IMAGE_CACHE,
+                       ClearFlags.DOM_STORAGES,
+                       ClearFlags.AUTH_SESSIONS,
+                       ClearFlags.PERMISSIONS,
+                       ClearFlags.ALL_CACHES,
+                       ClearFlags.SITE_SETTINGS,
+                       ClearFlags.SITE_DATA,
+                       ClearFlags.ALL })
+    /* package */ @interface StorageControllerClearFlags {}
+
+    /**
+     * Clear data for all hosts.
+     *
+     * 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.
+     *
+     * @param flags Combination of {@link ClearFlags}.
+     * @return A {@link GeckoResult} that will complete when clearing has
+     *         finished.
+     */
+    @AnyThread
+    public @NonNull GeckoResult<Void> clearData(
+            final @StorageControllerClearFlags long flags) {
+        final CallbackResult<Void> result = new CallbackResult<Void>() {
+            @Override
+            public void sendSuccess(final Object response) {
+                complete(null);
+            }
+        };
+
+        final GeckoBundle bundle = new GeckoBundle(1);
+        bundle.putLong("flags", flags);
+
+        EventDispatcher.getInstance().dispatch(
+            "GeckoView:ClearData", bundle, result);
+
+        return result;
+    }
+
+    /**
+     * Clear data owned by the given host.
+     * Clearing data for a host will not clear data created by its third-party
+     * origins.
+     *
+     * 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.
+     *
+     * @param host The host to be used.
+     * @param flags Combination of {@link ClearFlags}.
+     * @return A {@link GeckoResult} that will complete when clearing has
+     *         finished.
+     */
+    @AnyThread
+    public @NonNull GeckoResult<Void> clearDataFromHost(
+            final @NonNull String host,
+            final @StorageControllerClearFlags long flags) {
+        final CallbackResult<Void> result = new CallbackResult<Void>() {
+            @Override
+            public void sendSuccess(final Object response) {
+                complete(null);
+            }
+        };
+
+        final GeckoBundle bundle = new GeckoBundle(2);
+        bundle.putString("host", host);
+        bundle.putLong("flags", flags);
+
+        EventDispatcher.getInstance().dispatch(
+            "GeckoView:ClearHostData", bundle, result);
+
+        return result;
+    }
+}
--- 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
@@ -95,16 +95,20 @@ exclude: true
 [68.23]: ../GeckoSession.PermissionDelegate.html#PERMISSION_PERSISTENT_STORAGE
 
 - Added [`setVerticalClipping`][68.23] to [`GeckoDisplay`][68.24] and
   [`GeckoView`][68.23] to tell Gecko how much of its vertical space is clipped.
 
 [68.23]: ./GeckoView.html#setVerticalClipping-int-
 [68.24]: ./GeckoDisplay.html#setVerticalClipping-int-
 
+- Added [`StorageController`][68.25] API for clearing data.
+
+[68.25]: ../StorageController.html
+
 ## 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
 
@@ -301,9 +305,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]: dfd66add2059abb3318cfffbfc60001d2e25efb2
+[api-version]: 6078967e45c80550c5d17189856d3d3206b8540f
new file mode 100644
--- /dev/null
+++ b/mobile/android/modules/geckoview/GeckoViewStorageController.jsm
@@ -0,0 +1,122 @@
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["GeckoViewStorageController"];
+
+const {GeckoViewUtils} = ChromeUtils.import("resource://gre/modules/GeckoViewUtils.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const {debug, warn} = GeckoViewUtils.initLogging("GeckoViewStorageController"); // eslint-disable-line no-unused-vars
+
+// Keep in sync with StorageController.ClearFlags and nsIClearDataService.idl.
+const ClearFlags = [
+  [
+    // COOKIES
+    (1 << 0),
+    Ci.nsIClearDataService.CLEAR_COOKIES |
+    Ci.nsIClearDataService.CLEAR_PLUGIN_DATA |
+    Ci.nsIClearDataService.CLEAR_MEDIA_DEVICES,
+  ],
+  [
+    // NETWORK_CACHE
+    (1 << 1),
+    Ci.nsIClearDataService.CLEAR_NETWORK_CACHE,
+  ],
+  [
+    // IMAGE_CACHE
+    (1 << 2),
+    Ci.nsIClearDataService.CLEAR_IMAGE_CACHE,
+  ],
+  [
+    // HISTORY
+    (1 << 3),
+    Ci.nsIClearDataService.CLEAR_HISTORY |
+    Ci.nsIClearDataService.CLEAR_SESSION_HISTORY |
+    Ci.nsIClearDataService.CLEAR_STORAGE_ACCESS,
+  ],
+  [
+    // DOM_STORAGES
+    (1 << 4),
+    Ci.nsIClearDataService.CLEAR_APPCACHE |
+    Ci.nsIClearDataService.CLEAR_DOM_QUOTA |
+    Ci.nsIClearDataService.CLEAR_PUSH_NOTIFICATIONS |
+    Ci.nsIClearDataService.CLEAR_REPORTS,
+  ],
+  [
+    // AUTH_SESSIONS
+    (1 << 5),
+    Ci.nsIClearDataService.CLEAR_AUTH_TOKENS |
+    Ci.nsIClearDataService.CLEAR_AUTH_CACHE,
+  ],
+  [
+    // PERMISSIONS
+    (1 << 6),
+    Ci.nsIClearDataService.CLEAR_PERMISSIONS,
+  ],
+  [
+    // SITE_SETTINGS
+    (1 << 7),
+    Ci.nsIClearDataService.CLEAR_CONTENT_PREFERENCES |
+    Ci.nsIClearDataService.CLEAR_DOM_PUSH_NOTIFICATIONS |
+    Ci.nsIClearDataService.CLEAR_SECURITY_SETTINGS,
+  ],
+  [
+    // SITE_DATA
+    (1 << 8),
+    Ci.nsIClearDataService.CLEAR_EME,
+  ],
+  [
+    // ALL
+    (1 << 9),
+    Ci.nsIClearDataService.CLEAR_ALL,
+  ],
+];
+
+function convertFlags(aJavaFlags) {
+  const flags = ClearFlags.filter(cf => {
+    return cf[0] & aJavaFlags;
+  }).reduce((acc, cf) => {
+    return acc | cf[1];
+  }, 0);
+  return flags /* TODO: fix bug 1553454 */ & ~Ci.nsIClearDataService.CLEAR_HISTORY;
+}
+
+const GeckoViewStorageController = {
+  onEvent(aEvent, aData, aCallback) {
+    debug `onEvent ${aEvent} ${aData}`;
+
+    switch (aEvent) {
+      case "GeckoView:ClearData": {
+        this.clearData(aData.flags, aCallback);
+        break;
+      }
+      case "GeckoView:ClearHostData": {
+        this.clearHostData(aData.host, aData.flags, aCallback);
+        break;
+      }
+    }
+  },
+
+  clearData(aFlags, aCallback) {
+    new Promise(resolve => {
+      Services.clearData.deleteData(convertFlags(aFlags), resolve);
+    }).then(resultFlags => {
+      aCallback.onSuccess();
+    });
+  },
+
+  clearHostData(aHost, aFlags, aCallback) {
+    new Promise(resolve => {
+      Services.clearData.deleteDataFromHost(
+        aHost,
+        /* isUserRequest */ true,
+        convertFlags(aFlags),
+        resolve);
+    }).then(resultFlags => {
+      aCallback.onSuccess();
+    });
+  },
+};
--- a/mobile/android/modules/geckoview/moz.build
+++ b/mobile/android/modules/geckoview/moz.build
@@ -15,15 +15,16 @@ EXTRA_JS_MODULES += [
     'GeckoViewContent.jsm',
     'GeckoViewContentBlocking.jsm',
     'GeckoViewMedia.jsm',
     'GeckoViewModule.jsm',
     'GeckoViewNavigation.jsm',
     'GeckoViewProgress.jsm',
     'GeckoViewRemoteDebugger.jsm',
     'GeckoViewSettings.jsm',
+    'GeckoViewStorageController.jsm',
     'GeckoViewTab.jsm',
     'GeckoViewTelemetry.jsm',
     'GeckoViewUtils.jsm',
     'GeckoViewWebExtension.jsm',
     'LoadURIDelegate.jsm',
     'Messaging.jsm',
 ]