Bug 1489669 - [1.4] Add Storage Controller API. r=baku,snorp
authorEugen Sawin <esawin@me73.com>
Wed, 22 May 2019 20:21:09 +0000
changeset 475078 1912d221317a9ba513e087d4775b105d95856078
parent 475077 688e84498d3807b881d14645460e3015bbcbca19
child 475079 582dab79ef68b988cac90eaa2d00c444404bdc68
push id113193
push userdvarga@mozilla.com
push dateThu, 23 May 2019 16:04:39 +0000
treeherdermozilla-inbound@386097a10f84 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, snorp
bugs1489669
milestone69.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 1489669 - [1.4] Add Storage Controller API. r=baku,snorp 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',
 ]