Bug 1362448 - Support "incognito" in downloads.create draft
authorRob Wu <rob@robwu.nl>
Fri, 14 Jul 2017 17:14:18 +0200
changeset 620780 7e5647665a2a9cac3b19082228a4519413cf193a
parent 620658 36ad88e6b7b248c2f2ae59b80477e5474dd653dc
child 640807 7483a00970aeea5eb014599c5d9992d95ca0b0bb
push id72154
push userbmo:rob@robwu.nl
push dateThu, 03 Aug 2017 22:15:47 +0000
bugs1362448
milestone57.0a1
Bug 1362448 - Support "incognito" in downloads.create MozReview-Commit-ID: HN3x6eFT9xB
toolkit/components/extensions/ext-downloads.js
toolkit/components/extensions/schemas/downloads.json
toolkit/components/extensions/test/xpcshell/test_ext_downloads_download.js
toolkit/components/extensions/test/xpcshell/test_ext_downloads_private.js
toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
--- a/toolkit/components/extensions/ext-downloads.js
+++ b/toolkit/components/extensions/ext-downloads.js
@@ -522,16 +522,17 @@ this.downloads = class extends Extension
           }
 
           let download;
           return Downloads.getPreferredDownloadsDirectory()
             .then(downloadsDir => createTarget(downloadsDir))
             .then(target => {
               const source = {
                 url: options.url,
+                isPrivate: options.incognito,
               };
 
               if (options.method || options.headers || options.body) {
                 source.adjustChannel = adjustChannel;
               }
 
               return Downloads.createDownload({
                 source,
--- a/toolkit/components/extensions/schemas/downloads.json
+++ b/toolkit/components/extensions/schemas/downloads.json
@@ -372,16 +372,22 @@
                 "type": "string",
                 "format": "url"
               },
               "filename": {
                 "description": "A file path relative to the Downloads directory to contain the downloaded file.",
                 "optional": true,
                 "type": "string"
               },
+              "incognito": {
+                "description": "Whether to associate the download with a private browsing session.",
+                "optional": true,
+                "default": false,
+                "type": "boolean"
+              },
               "conflictAction": {
                 "$ref": "FilenameConflictAction",
                 "optional": true
               },
               "saveAs": {
                 "description": "Use a file-chooser to allow the user to select a filename.",
                 "optional": true,
                 "type": "boolean"
--- a/toolkit/components/extensions/test/xpcshell/test_ext_downloads_download.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_downloads_download.js
@@ -261,16 +261,27 @@ add_task(async function test_downloads()
   }, "download", BLOB_STRING.length, "blob url with no filename");
   extension.sendMessage("killTheBlob");
 
   // Download a normal URL with an empty filename part.
   await testDownload({
     url: BASE + "dir/",
   }, "download", 8, "normal url with empty filename");
 
+  // Check that the "incognito" property is supported.
+  await testDownload({
+    url: FILE_URL,
+    incognito: false,
+  }, FILE_NAME, FILE_LEN, "incognito=false");
+
+  await testDownload({
+    url: FILE_URL,
+    incognito: true,
+  }, FILE_NAME, FILE_LEN, "incognito=true");
+
   await extension.unload();
 });
 
 add_task(async function test_download_post() {
   const server = createHttpServer();
   const url = `http://localhost:${server.identity.primaryPort}/post-log`;
 
   let received;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_downloads_private.js
@@ -0,0 +1,116 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+Cu.import("resource://gre/modules/Downloads.jsm");
+
+const server = createHttpServer();
+server.registerDirectory("/data/", do_get_file("data"));
+
+const BASE = `http://localhost:${server.identity.primaryPort}/data`;
+const TXT_FILE = "file_download.txt";
+const TXT_URL = BASE + "/" + TXT_FILE;
+
+function setup() {
+  let downloadDir = FileUtils.getDir("TmpD", ["downloads"]);
+  downloadDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+  do_print(`Using download directory ${downloadDir.path}`);
+
+  Services.prefs.setIntPref("browser.download.folderList", 2);
+  Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, downloadDir);
+
+  do_register_cleanup(() => {
+    Services.prefs.clearUserPref("browser.download.folderList");
+    Services.prefs.clearUserPref("browser.download.dir");
+
+    let entries = downloadDir.directoryEntries;
+    while (entries.hasMoreElements()) {
+      let entry = entries.getNext().QueryInterface(Ci.nsIFile);
+      ok(false, `Leftover file ${entry.path} in download directory`);
+      entry.remove(false);
+    }
+
+    downloadDir.remove(false);
+  });
+}
+
+add_task(async function test_private_download() {
+  setup();
+
+  let extension = ExtensionTestUtils.loadExtension({
+    background: async function() {
+      function promiseEvent(eventTarget, accept) {
+        return new Promise(resolve => {
+          eventTarget.addListener(function listener(data) {
+            if (accept && !accept(data)) {
+              return;
+            }
+            eventTarget.removeListener(listener);
+            resolve(data);
+          });
+        });
+      }
+      let startTestPromise = promiseEvent(browser.test.onMessage);
+      let onCreatedPromise = promiseEvent(browser.downloads.onCreated);
+      let onDonePromise = promiseEvent(browser.downloads.onChanged,
+        delta => delta.state && delta.state.current === "complete");
+
+      browser.test.sendMessage("ready");
+      let {url, filename} = await startTestPromise;
+
+      browser.test.log("Starting private download");
+      let downloadId = await browser.downloads.download({
+        url,
+        filename,
+        incognito: true,
+      });
+
+      browser.test.log("Waiting for downloads.onCreated");
+      let createdItem = await onCreatedPromise;
+
+      browser.test.log("Waiting for completion notification");
+      await onDonePromise;
+
+      // test_ext_downloads_download.js already tests whether the file exists
+      // in the file system. Here we will only verify that the  downloads API
+      // behaves in a meaningful way.
+
+      let [downloadItem] = await browser.downloads.search({id: downloadId});
+      browser.test.assertEq(url, createdItem.url, "onCreated url should match");
+      browser.test.assertEq(url, downloadItem.url, "download url should match");
+      browser.test.assertTrue(createdItem.incognito,
+        "created download should be private");
+      browser.test.assertTrue(downloadItem.incognito,
+        "stored download should be private");
+
+      browser.test.log("Removing downloaded file");
+      browser.test.assertTrue(downloadItem.exists, "downloaded file exists");
+      await browser.downloads.removeFile(downloadId);
+
+      // Disabled because the assertion fails - https://bugzil.la/1381031
+      // let [downloadItem2] = await browser.downloads.search({id: downloadId});
+      // browser.test.assertFalse(downloadItem2.exists, "file should be deleted");
+
+      browser.test.log("Erasing private download from history");
+      let erasePromise = promiseEvent(browser.downloads.onErased);
+      await browser.downloads.erase({id: downloadId});
+      browser.test.assertEq(downloadId, await erasePromise,
+        "onErased should be fired for the erased private download");
+
+      browser.test.notifyPass("private download test done");
+    },
+    manifest: {
+      permissions: ["downloads"],
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitMessage("ready");
+  extension.sendMessage({
+    url: TXT_URL,
+    filename: TXT_FILE,
+  });
+
+  await extension.awaitFinish("private download test done");
+  await extension.unload();
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -17,16 +17,18 @@ skip-if = os == "android"
 [test_ext_contextual_identities.js]
 skip-if = os == "android" # Containers are not exposed to android.
 [test_ext_debugging_utils.js]
 [test_ext_downloads.js]
 [test_ext_downloads_download.js]
 skip-if = os == "android"
 [test_ext_downloads_misc.js]
 skip-if = os == "android" || (os=='linux' && bits==32) # linux32: bug 1324870
+[test_ext_downloads_private.js]
+skip-if = os == "android"
 [test_ext_downloads_search.js]
 skip-if = os == "android"
 [test_ext_experiments.js]
 [test_ext_extension.js]
 [test_ext_extensionPreferencesManager.js]
 [test_ext_extensionSettingsStore.js]
 [test_ext_extension_content_telemetry.js]
 skip-if = os == "android" # checking for telemetry needs to be updated: 1384923