Bug 1271345 Fix brower.download.download() on blob: urls r?kmag draft
authorAndrew Swan <aswan@mozilla.com>
Tue, 31 May 2016 11:42:41 -0700
changeset 374277 c8186e96a8e1298be725331e6da56417301cd20b
parent 372909 3435dd7ad71fe9003bdeee18fd38d815e033beef
child 522594 d546909599bef197cb43eff7acdb0d5367593dda
push id19974
push useraswan@mozilla.com
push dateThu, 02 Jun 2016 04:19:29 +0000
reviewerskmag
bugs1271345
milestone49.0a1
Bug 1271345 Fix brower.download.download() on blob: urls r?kmag Calling download() on a blob URL was failing in schema validation since we weren't propagating the extension principal all the way to the call to scriptSecurityManager.checkLoadURI... MozReview-Commit-ID: JgEnQ6yxO4P
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ext-downloads.js
toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_download.html
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -541,16 +541,20 @@ GlobalManager = {
 
     let schemaApi = Management.generateAPIs(extension, context, Management.schemaApis, namespaces);
 
     // Add in any extra API namespaces which do not have implementations
     // outside of their schema file.
     schemaApi.extensionTypes = {};
 
     let schemaWrapper = {
+      get principal() {
+        return context.principal;
+      },
+
       get cloneScope() {
         return context.cloneScope;
       },
 
       callFunction(path, name, args) {
         return findPathInObject(schemaApi, path)[name](...args);
       },
 
--- a/toolkit/components/extensions/ext-downloads.js
+++ b/toolkit/components/extensions/ext-downloads.js
@@ -415,18 +415,23 @@ extensions.registerSchemaAPI("downloads"
         function createTarget(downloadsDir) {
           // TODO
           // if (options.saveAs) { }
 
           let target;
           if (options.filename) {
             target = OS.Path.join(downloadsDir, options.filename);
           } else {
-            let uri = NetUtil.newURI(options.url).QueryInterface(Ci.nsIURL);
-            target = OS.Path.join(downloadsDir, uri.fileName);
+            let uri = NetUtil.newURI(options.url);
+
+            let filename;
+            if (uri instanceof Ci.nsIURL) {
+              filename = uri.fileName;
+            }
+            target = OS.Path.join(downloadsDir, filename || "download");
           }
 
           // This has a race, something else could come along and create
           // the file between this test and them time the download code
           // creates the target file.  But we can't easily fix it without
           // modifying DownloadCore so we live with it for now.
           return OS.File.exists(target).then(exists => {
             if (exists) {
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_download.html
+++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_download.html
@@ -46,23 +46,39 @@ function setup() {
 
   SimpleTest.registerCleanupFunction(() => {
     Services.prefs.clearUserPref("browser.download.folderList");
     Services.prefs.clearUserPref("browser.download.dir");
   });
 }
 
 function backgroundScript() {
-  browser.test.onMessage.addListener(function(msg) {
+  let blobUrl;
+  browser.test.onMessage.addListener((msg, ...args) => {
     if (msg == "download.request") {
+      let options = args[0];
+
+      if (options.blobme) {
+        let blob = new Blob(options.blobme);
+        delete options.blobme;
+        blobUrl = options.url = window.URL.createObjectURL(blob);
+      }
+
       // download() throws on bad arguments, we can remove the extra
       // promise when bug 1250223 is fixed.
-      return Promise.resolve().then(() => browser.downloads.download(arguments[1]))
-             .then((id) => browser.test.sendMessage("download.done", {status: "success", id}))
-             .catch(error => browser.test.sendMessage("download.done", {status: "error", errmsg: error.message}));
+      return Promise.resolve().then(() => browser.downloads.download(options))
+                    .then(id => {
+                      browser.test.sendMessage("download.done", {status: "success", id});
+                    })
+                    .catch(error => {
+                      browser.test.sendMessage("download.done", {status: "error", errmsg: error.message});
+                    });
+    } else if (msg == "killTheBlob") {
+      window.URL.revokeObjectURL(blobUrl);
+      blobUrl = null;
     }
   });
 
   browser.test.sendMessage("ready");
 }
 
 // This function is a bit of a sledgehammer, it looks at every download
 // the browser knows about and waits for all active downloads to complete.
@@ -195,16 +211,30 @@ add_task(function* test_downloads() {
   yield download({
     url: FILE_URL,
     filename: OS.Path.join("foo", "..", "..", "file_download.txt"),
   }).then(msg => {
     is(msg.status, "error", "downloads.download() fails with back-references");
     is(msg.errmsg, "filename must not contain back-references (..)", "error message for back-references is correct");
   });
 
+  // Try to download a blob url
+  const BLOB_STRING = "Hello, world";
+  yield testDownload({
+    blobme: [BLOB_STRING],
+    filename: FILE_NAME,
+  }, FILE_NAME, BLOB_STRING.length, "blob url");
+  extension.sendMessage("killTheBlob");
+
+  // Try to download a blob url without a given filename
+  yield testDownload({
+    blobme: [BLOB_STRING],
+  }, "download", BLOB_STRING.length, "blob url with no filename");
+  extension.sendMessage("killTheBlob");
+
   yield extension.unload();
 });
 
 // check for leftover files in the download directory
 add_task(function* () {
   let entries = downloadDir.directoryEntries;
   while (entries.hasMoreElements()) {
     let entry = entries.getNext().QueryInterface(Ci.nsIFile);