Bug 1321303 - Part 5: Implement browsingData.removeFormData, r=aswan
authorBob Silverberg <bsilverberg@mozilla.com>
Wed, 14 Dec 2016 14:29:57 -0500
changeset 377744 4b819370879335a9dddac438e6ad692b6ed7ceca
parent 377743 d8db138a2d4e1de01d9c5ea434e5b4e67650279f
child 377745 95426f1151bcfe155c32bb7b4af580012d04fe3b
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1321303
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 1321303 - Part 5: Implement browsingData.removeFormData, r=aswan MozReview-Commit-ID: aK6m7vl3i1
browser/components/extensions/ext-browsingData.js
browser/components/extensions/schemas/browsing_data.json
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_browsingData_formData.js
--- a/browser/components/extensions/ext-browsingData.js
+++ b/browser/components/extensions/ext-browsingData.js
@@ -67,16 +67,20 @@ let clearCookies = Task.async(function* 
     cookieMgr.removeAll();
   }
 });
 
 function clearDownloads(options) {
   return sanitizer.items.downloads.clear(makeRange(options));
 }
 
+function clearFormData(options) {
+  return sanitizer.items.formdata.clear(makeRange(options));
+}
+
 function clearHistory(options) {
   return sanitizer.items.history.clear(makeRange(options));
 }
 
 function doRemoval(options, dataToRemove, extension) {
   if (options.originTypes &&
       (options.originTypes.protectedWeb || options.originTypes.extension)) {
     return Promise.reject(
@@ -92,16 +96,19 @@ function doRemoval(options, dataToRemove
           removalPromises.push(clearCache());
           break;
         case "cookies":
           removalPromises.push(clearCookies(options));
           break;
         case "downloads":
           removalPromises.push(clearDownloads(options));
           break;
+        case "formData":
+          removalPromises.push(clearFormData(options));
+          break;
         case "history":
           removalPromises.push(clearHistory(options));
           break;
         default:
           invalidDataTypes.push(dataType);
       }
     }
   }
@@ -149,14 +156,17 @@ extensions.registerSchemaAPI("browsingDa
         return doRemoval(options, {cache: true});
       },
       removeCookies(options) {
         return doRemoval(options, {cookies: true});
       },
       removeDownloads(options) {
         return doRemoval(options, {downloads: true});
       },
+      removeFormData(options) {
+        return doRemoval(options, {formData: true});
+      },
       removeHistory(options) {
         return doRemoval(options, {history: true});
       },
     },
   };
 });
--- a/browser/components/extensions/schemas/browsing_data.json
+++ b/browser/components/extensions/schemas/browsing_data.json
@@ -272,17 +272,16 @@
           }
         ]
       },
       {
         "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",
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -23,16 +23,17 @@ support-files =
 [browser_ext_browserAction_context.js]
 [browser_ext_browserAction_disabled.js]
 [browser_ext_browserAction_pageAction_icon.js]
 [browser_ext_browserAction_pageAction_icon_permissions.js]
 [browser_ext_browserAction_popup.js]
 [browser_ext_browserAction_popup_preload.js]
 [browser_ext_browserAction_popup_resize.js]
 [browser_ext_browserAction_simple.js]
+[browser_ext_browsingData_formData.js]
 [browser_ext_browsingData_history.js]
 [browser_ext_commands_execute_browser_action.js]
 [browser_ext_commands_execute_page_action.js]
 [browser_ext_commands_getAll.js]
 [browser_ext_commands_onCommand.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_contextMenus.js]
 [browser_ext_contextMenus_checkboxes.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_browsingData_formData.js
@@ -0,0 +1,138 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
+                                  "resource://gre/modules/FormHistory.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
+
+const REFERENCE_DATE = Date.now();
+
+function countEntries(fieldname, message, expected) {
+  return new Promise((resolve, reject) => {
+    let callback = {
+      handleResult: result => {
+        is(result, expected, message);
+        resolve();
+      },
+      handleError: reject,
+    };
+
+    FormHistory.count({fieldname}, callback);
+  });
+}
+
+async function setupFormHistory() {
+  function searchEntries(terms, params) {
+    return new Promise((resolve, reject) => {
+      let callback = {
+        handleResult: resolve,
+        handleError: reject,
+      };
+
+      FormHistory.search(terms, params, callback);
+    });
+  }
+
+  function update(changes) {
+    return new Promise((resolve, reject) => {
+      let callback = {
+        handleError: reject,
+        handleCompletion: resolve,
+      };
+      FormHistory.update(changes, callback);
+    });
+  }
+
+  // Make sure we've got a clean DB to start with, then add the entries we'll be testing.
+  await update([
+    {op: "remove"},
+    {
+      op: "add",
+      fieldname: "reference",
+      value: "reference",
+    }, {
+      op: "add",
+      fieldname: "10secondsAgo",
+      value: "10s",
+    }, {
+      op: "add",
+      fieldname: "10minutesAgo",
+      value: "10m",
+    }]);
+
+  // Age the entries to the proper vintage.
+  let timestamp = PlacesUtils.toPRTime(REFERENCE_DATE);
+  let result = await searchEntries(["guid"], {fieldname: "reference"});
+  await update({op: "update", firstUsed: timestamp, guid: result.guid});
+
+  timestamp = PlacesUtils.toPRTime(REFERENCE_DATE - 10000);
+  result = await searchEntries(["guid"], {fieldname: "10secondsAgo"});
+  await update({op: "update", firstUsed: timestamp, guid: result.guid});
+
+  timestamp = PlacesUtils.toPRTime(REFERENCE_DATE - 10000 * 60);
+  result = await searchEntries(["guid"], {fieldname: "10minutesAgo"});
+  await update({op: "update", firstUsed: timestamp, guid: result.guid});
+
+  // Sanity check.
+  await countEntries("reference", "Checking for 10minutes form history entry creation", 1);
+  await countEntries("10secondsAgo", "Checking for 1hour form history entry creation", 1);
+  await countEntries("10minutesAgo", "Checking for 1hour10minutes form history entry creation", 1);
+}
+
+add_task(async function testFormData() {
+  function background() {
+    browser.test.onMessage.addListener(async (msg, options) => {
+      if (msg == "removeFormData") {
+        await browser.browsingData.removeFormData(options);
+      } else {
+        await browser.browsingData.remove(options, {formData: true});
+      }
+      browser.test.sendMessage("formDataRemoved");
+    });
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    background,
+    manifest: {
+      permissions: ["browsingData"],
+    },
+  });
+
+  async function testRemovalMethod(method) {
+    // Clear form data with no since value.
+    await setupFormHistory();
+    extension.sendMessage(method, {});
+    await extension.awaitMessage("formDataRemoved");
+
+    await countEntries("reference", "reference form entry should be deleted.", 0);
+    await countEntries("10secondsAgo", "10secondsAgo form entry should be deleted.", 0);
+    await countEntries("10minutesAgo", "10minutesAgo form entry should be deleted.", 0);
+
+    // Clear form data with recent since value.
+    await setupFormHistory();
+    extension.sendMessage(method, {since: REFERENCE_DATE});
+    await extension.awaitMessage("formDataRemoved");
+
+    await countEntries("reference", "reference form entry should be deleted.", 0);
+    await countEntries("10secondsAgo", "10secondsAgo form entry should still exist.", 1);
+    await countEntries("10minutesAgo", "10minutesAgo form entry should still exist.", 1);
+
+    // Clear form data with old since value.
+    await setupFormHistory();
+    extension.sendMessage(method, {since: REFERENCE_DATE - 1000000});
+    await extension.awaitMessage("formDataRemoved");
+
+    await countEntries("reference", "reference form entry should be deleted.", 0);
+    await countEntries("10secondsAgo", "10secondsAgo form entry should be deleted.", 0);
+    await countEntries("10minutesAgo", "10minutesAgo form entry should be deleted.", 0);
+  }
+
+  await extension.startup();
+
+  await testRemovalMethod("removeFormData");
+  await testRemovalMethod("remove");
+
+  await extension.unload();
+});