Bug 1320986 - Implement browsingData.settings WebExtensions API method, r?aswan draft
authorBob Silverberg <bsilverberg@mozilla.com>
Mon, 28 Nov 2016 09:17:04 -0500
changeset 446648 440fcb7527ceba7cb3460f59e3de9e5f343a360b
parent 444725 8387a4ada9a5c4cab059d8fafe0f8c933e83c149
child 446649 d826910a1d04d2aa4ed30294914f2ceeef8d084f
push id37842
push userbmo:bob.silverberg@gmail.com
push dateThu, 01 Dec 2016 18:06:59 +0000
reviewersaswan
bugs1320986
milestone53.0a1
Bug 1320986 - Implement browsingData.settings WebExtensions API method, r?aswan MozReview-Commit-ID: JqUm77vFod2
browser/components/extensions/schemas/browsing_data.json
toolkit/components/extensions/ext-browsingData.js
toolkit/components/extensions/extensions-toolkit.manifest
toolkit/components/extensions/jar.mn
toolkit/components/extensions/schemas/jar.mn
toolkit/components/extensions/schemas/manifest.json
toolkit/components/extensions/test/xpcshell/test_ext_browsingData_settings.js
toolkit/components/extensions/test/xpcshell/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/schemas/browsing_data.json
@@ -0,0 +1,422 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[
+  {
+    "namespace": "browsingData",
+    "description": "Use the <code>chrome.browsingData</code> API to remove browsing data from a user's local profile.",
+    "permissions": ["browsingData"],
+    "types": [
+      {
+        "id": "RemovalOptions",
+        "type": "object",
+        "description": "Options that determine exactly what data will be removed.",
+        "properties": {
+          "since": {
+            "type": "number",
+            "optional": true,
+            "description": "Remove data accumulated on or after this date, represented in milliseconds since the epoch (accessible via the <code>getTime</code> method of the JavaScript <code>Date</code> object). If absent, defaults to 0 (which would remove all browsing data)."
+          },
+          "originTypes": {
+            "type": "object",
+            "optional": true,
+            "description": "An object whose properties specify which origin types ought to be cleared. If this object isn't specified, it defaults to clearing only \"unprotected\" origins. Please ensure that you <em>really</em> want to remove application data before adding 'protectedWeb' or 'extensions'.",
+            "properties": {
+              "unprotectedWeb": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Normal websites."
+              },
+              "protectedWeb": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Websites that have been installed as hosted applications (be careful!)."
+              },
+              "extension": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Extensions and packaged applications a user has installed (be _really_ careful!)."
+              }
+            }
+          }
+        }
+      },
+      {
+        "id": "DataTypeSet",
+        "type": "object",
+        "description": "A set of data types. Missing data types are interpreted as <code>false</code>.",
+        "properties": {
+          "appcache": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Websites' appcaches."
+          },
+          "cache": {
+            "type": "boolean",
+            "optional": true,
+            "description": "The browser's cache. Note: when removing data, this clears the <em>entire</em> cache: it is not limited to the range you specify."
+          },
+          "cookies": {
+            "type": "boolean",
+            "optional": true,
+            "description": "The browser's cookies."
+          },
+          "downloads": {
+            "type": "boolean",
+            "optional": true,
+            "description": "The browser's download list."
+          },
+          "fileSystems": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Websites' file systems."
+          },
+          "formData": {
+            "type": "boolean",
+            "optional": true,
+            "description": "The browser's stored form data."
+          },
+          "history": {
+            "type": "boolean",
+            "optional": true,
+            "description": "The browser's history."
+          },
+          "indexedDB": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Websites' IndexedDB data."
+          },
+          "localStorage": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Websites' local storage data."
+          },
+          "serverBoundCertificates": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Server-bound certificates."
+          },
+          "passwords": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Stored passwords."
+          },
+          "pluginData": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Plugins' data."
+          },
+          "serviceWorkers": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Service Workers."
+          },
+          "webSQL": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Websites' WebSQL data."
+          }
+        }
+      }
+    ],
+    "functions": [
+      {
+        "name": "settings",
+        "description": "Reports which types of data are currently selected in the 'Clear browsing data' settings UI.  Note: some of the data types included in this API are not available in the settings UI, and some UI settings control more than one data type listed here.",
+        "type": "function",
+        "async": "callback",
+        "parameters": [
+          {
+            "name": "callback",
+            "type": "function",
+            "parameters": [
+              {
+                "name": "result",
+                "type": "object",
+                "properties": {
+                  "options": {
+                    "$ref": "RemovalOptions"
+                  },
+                  "dataToRemove": {
+                    "$ref": "DataTypeSet",
+                    "description": "All of the types will be present in the result, with values of <code>true</code> if they are both selected to be removed and permitted to be removed, otherwise <code>false</code>."
+                  },
+                  "dataRemovalPermitted": {
+                    "$ref": "DataTypeSet",
+                    "description": "All of the types will be present in the result, with values of <code>true</code> if they are permitted to be removed (e.g., by enterprise policy) and <code>false</code> if not."
+                  }
+                }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "remove",
+        "description": "Clears various types of browsing data stored in a user's profile.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "dataToRemove",
+            "$ref": "DataTypeSet",
+            "description": "The set of data types to remove."
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when deletion has completed.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeAppcache",
+        "description": "Clears websites' appcache data.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when websites' appcache data has been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeCache",
+        "description": "Clears the browser's cache.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when the browser's cache has been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeCookies",
+        "description": "Clears the browser's cookies and server-bound certificates modified within a particular timeframe.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when the browser's cookies and server-bound certificates have been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeDownloads",
+        "description": "Clears the browser's list of downloaded files (<em>not</em> the downloaded files themselves).",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when the browser's list of downloaded files has been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeFileSystems",
+        "description": "Clears websites' file system data.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when websites' file systems have been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeFormData",
+        "description": "Clears the browser's stored form data (autofill).",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when the browser's form data has been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeHistory",
+        "description": "Clears the browser's history.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when the browser's history has cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeIndexedDB",
+        "description": "Clears websites' IndexedDB data.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when websites' IndexedDB data has been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeLocalStorage",
+        "description": "Clears websites' local storage data.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when websites' local storage has been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removePluginData",
+        "description": "Clears plugins' data.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when plugins' data has been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removePasswords",
+        "description": "Clears the browser's stored passwords.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when the browser's passwords have been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeWebSQL",
+        "description": "Clears websites' WebSQL data.",
+        "type": "function",
+        "async": "callback",
+        "unsupported": true,
+        "parameters": [
+          {
+            "$ref": "RemovalOptions",
+            "name": "options"
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "description": "Called when websites' WebSQL databases have been cleared.",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      }
+    ]
+  }
+]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/ext-browsingData.js
@@ -0,0 +1,48 @@
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetter(this, "Sanitizer",
+                                  "resource:///modules/Sanitizer.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+                                  "resource://gre/modules/Services.jsm");
+
+const PREF_DOMAIN = "privacy.cpd.";
+const PREF_LIST = ["cache", "cookies", "history", "formdata", "downloads"];
+
+function getBoolPref(branch, name) {
+  let pref;
+  try {
+    pref = branch.getBoolPref(name);
+  } catch (ex) {
+    pref = false;
+  }
+  return pref;
+}
+
+extensions.registerSchemaAPI("browsingData", "addon_parent", context => {
+  return {
+    browsingData: {
+      settings: function() {
+        // since will be the start of what is returned by Sanitizer.getClearRange
+        // divided by 1000 to convert to ms.
+        let since = Sanitizer.getClearRange()[0] / 1000;
+        let options = {since};
+
+        let branch = Services.prefs.getBranch(PREF_DOMAIN);
+        let dataToRemove = {};
+        let dataRemovalPermitted = {};
+
+        for (let item of PREF_LIST) {
+          dataToRemove[item] = getBoolPref(branch, item);
+          // Firefox doesn't have the same concept of dataRemovalPermitted
+          // as Chrome, so it will always be true.
+          dataRemovalPermitted[item] = true;
+        }
+        // formData has a different case than the pref formdata.
+        dataToRemove.formData = getBoolPref(branch, "formdata");
+        dataRemovalPermitted.formData = true;
+
+        return Promise.resolve({options, dataToRemove, dataRemovalPermitted});
+      },
+    },
+  };
+});
--- a/toolkit/components/extensions/extensions-toolkit.manifest
+++ b/toolkit/components/extensions/extensions-toolkit.manifest
@@ -1,11 +1,12 @@
 # scripts
 category webextension-scripts alarms chrome://extensions/content/ext-alarms.js
 category webextension-scripts backgroundPage chrome://extensions/content/ext-backgroundPage.js
+category webextension-scripts browsingData chrome://extensions/content/ext-browsingData.js
 category webextension-scripts cookies chrome://extensions/content/ext-cookies.js
 category webextension-scripts downloads chrome://extensions/content/ext-downloads.js
 category webextension-scripts management chrome://extensions/content/ext-management.js
 category webextension-scripts notifications chrome://extensions/content/ext-notifications.js
 category webextension-scripts i18n chrome://extensions/content/ext-i18n.js
 category webextension-scripts idle chrome://extensions/content/ext-idle.js
 category webextension-scripts webRequest chrome://extensions/content/ext-webRequest.js
 category webextension-scripts webNavigation chrome://extensions/content/ext-webNavigation.js
@@ -36,16 +37,17 @@ category webextension-scripts-addon i18n
 category webextension-scripts-addon identity chrome://extensions/content/ext-c-identity.js
 #endif
 category webextension-scripts-addon runtime chrome://extensions/content/ext-c-runtime.js
 category webextension-scripts-addon test chrome://extensions/content/ext-c-test.js
 category webextension-scripts-addon storage chrome://extensions/content/ext-c-storage.js
 
 # schemas
 category webextension-schemas alarms chrome://extensions/content/schemas/alarms.json
+category webextension-schemas browsing_data chrome://extensions/content/schemas/browsing_data.json
 category webextension-schemas cookies chrome://extensions/content/schemas/cookies.json
 category webextension-schemas downloads chrome://extensions/content/schemas/downloads.json
 category webextension-schemas events chrome://extensions/content/schemas/events.json
 category webextension-schemas extension chrome://extensions/content/schemas/extension.json
 category webextension-schemas extension_types chrome://extensions/content/schemas/extension_types.json
 category webextension-schemas i18n chrome://extensions/content/schemas/i18n.json
 #ifndef ANDROID
 category webextension-schemas identity chrome://extensions/content/schemas/identity.json
--- a/toolkit/components/extensions/jar.mn
+++ b/toolkit/components/extensions/jar.mn
@@ -2,16 +2,17 @@
 # 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/.
 
 toolkit.jar:
 % content extensions %content/extensions/
     content/extensions/ext-alarms.js
     content/extensions/ext-backgroundPage.js
     content/extensions/ext-browser-content.js
+    content/extensions/ext-browsingData.js
     content/extensions/ext-cookies.js
     content/extensions/ext-downloads.js
     content/extensions/ext-management.js
     content/extensions/ext-notifications.js
     content/extensions/ext-i18n.js
     content/extensions/ext-idle.js
     content/extensions/ext-webRequest.js
     content/extensions/ext-webNavigation.js
--- a/toolkit/components/extensions/schemas/jar.mn
+++ b/toolkit/components/extensions/schemas/jar.mn
@@ -1,15 +1,16 @@
 # 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/.
 
 toolkit.jar:
 % content extensions %content/extensions/
     content/extensions/schemas/alarms.json
+    content/extensions/schemas/browsing_data.json
     content/extensions/schemas/cookies.json
     content/extensions/schemas/downloads.json
     content/extensions/schemas/events.json
     content/extensions/schemas/experiments.json
     content/extensions/schemas/extension.json
     content/extensions/schemas/extension_types.json
     content/extensions/schemas/i18n.json
 #ifndef ANDROID
--- a/toolkit/components/extensions/schemas/manifest.json
+++ b/toolkit/components/extensions/schemas/manifest.json
@@ -206,16 +206,17 @@
       },
       {
         "id": "Permission",
         "choices": [
           {
             "type": "string",
             "enum": [
               "alarms",
+              "browsingData",
               "clipboardWrite",
               "idle",
               "notifications",
               "storage"
             ]
           },
           { "$ref": "MatchPattern" }
         ]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_browsingData_settings.js
@@ -0,0 +1,73 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetter(this, "Sanitizer",
+                                  "resource:///modules/Sanitizer.jsm");
+
+const PREF_DOMAIN = "privacy.cpd.";
+
+add_task(function* testSettings() {
+  function background() {
+    browser.test.onMessage.addListener(msg => {
+      browser.browsingData.settings().then(settings => {
+        browser.test.sendMessage("settings", settings);
+      });
+    });
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    background,
+    manifest: {
+      permissions: ["browsingData"],
+    },
+  });
+
+  yield extension.startup();
+
+  let branch = Services.prefs.getBranch(PREF_DOMAIN);
+
+  extension.sendMessage("settings");
+  let settings = yield extension.awaitMessage("settings");
+
+  let since = Sanitizer.getClearRange()[0] / 1000;
+
+  // Because it is based on the current timestamp, we cannot know the exact
+  // value to expect for since, so allow a 1s variance.
+  ok(since - 500 < settings.options < since + 500,
+     "settings.options contains the expected since value");
+
+  let dataTypeSet = settings.dataToRemove;
+  for (let key of Object.keys(dataTypeSet)) {
+    let pref = (key == "formData") ? "formdata" : key;
+    equal(branch.getBoolPref(pref), dataTypeSet[key], `${key} property of dataToRemove matches the expected pref`);
+  }
+
+  dataTypeSet = settings.dataRemovalPermitted;
+  for (let key of Object.keys(dataTypeSet)) {
+    equal(true, dataTypeSet[key], `${key} property of dataRemovalPermitted is true`);
+  }
+
+  // Explicitly set a pref to both true and false and then check.
+  const SINGLE_PREF = "cache";
+
+  branch.setBoolPref(SINGLE_PREF, true);
+
+  extension.sendMessage("settings");
+  settings = yield extension.awaitMessage("settings");
+
+  equal(settings.dataToRemove[SINGLE_PREF], true, "pref that was set to true returns true");
+
+  branch.setBoolPref(SINGLE_PREF, false);
+
+  extension.sendMessage("settings");
+  settings = yield extension.awaitMessage("settings");
+
+  equal(settings.dataToRemove[SINGLE_PREF], false, "pref that was set to false returns false");
+
+  do_register_cleanup(() => {
+    branch.clearUserPref(SINGLE_PREF);
+  });
+
+  yield extension.unload();
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -19,16 +19,17 @@ tags = webextensions
 [test_ext_background_generated_reload.js]
 [test_ext_background_global_history.js]
 skip-if = os == "android" # Android does not use Places for history.
 [test_ext_background_private_browsing.js]
 [test_ext_background_runtime_connect_params.js]
 [test_ext_background_sub_windows.js]
 [test_ext_background_window_properties.js]
 skip-if = os == "android"
+[test_ext_browsingData_settings.js]
 [test_ext_contexts.js]
 [test_ext_downloads.js]
 [test_ext_downloads_download.js]
 skip-if = os == "android"
 [test_ext_downloads_misc.js]
 skip-if = os == "android"
 [test_ext_downloads_search.js]
 skip-if = os == "android"