Bug 1271345 Fix brower.download.download() on blob: urls r=kmag
authorAndrew Swan <aswan@mozilla.com>
Tue, 31 May 2016 11:42:41 -0700
changeset 341191 332438c705827bb0034d1bd05e227a5a363def8a
parent 341190 17fd76ee110ca58e6e2a518cee4f867087943433
child 341192 4724bebdf93ae536d98d434ce5753bd6f29a2d73
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1271345
milestone49.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 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);