2

I'm getting more and more downhearted. For the last three days, I've been trying to add to my simple Firefox add-on a 'download image' feature.

The following is an add-on that creates a right-click contextual menu with a sub-menu:

var contextMenu = require("sdk/context-menu");
var clipboard = require("sdk/clipboard");
var data = require("sdk/self").data;

var myApp_cm = contextMenu.Menu({
label:          "Send to myApp",
context:        contextMenu.SelectorContext("body"),
items: [

    contextMenu.Item({ 
        label:          "Send image to MyApp",
        context:        contextMenu.SelectorContext("img"),
        contentScript:  'self.on("click", function (node, data) { ' +
                        '  var link = node.src; ' +
                        '  self.postMessage(link); ' +
                        '});',
        onMessage:      function(link) {
                            //
                            // Download image from 'link' and run 'myApp.exe' with
                            // downloaded image as parameter
                            //
                        } 
        })
    ]
});

I would like to add to the above code a simple download feature as the "Save as..." option of Firefox that downloads the image from the selected URL and runs an EXE with the downloaded image as a parameter.

I read everything I found about this argument starting from Mozilla MDN to all the questions asked at Stackoverflow. But, I never managed to make a single line of code work. I really don't understand why it's so complicated to download a file when this is the browser's job.

For example, I know that from Firefox 26+ I need to use downloads.jsm. So, I copied the following code from MDN.

Components.utils.import("resource://gre/modules/Downloads.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm")
Components.utils.import("resource://gre/modules/Task.jsm");

Task.spawn(function () {
    yield Downloads.fetch("http://www.mozilla.org/",OS.Path.join(OS.Constants.Path.tmpDir,"example-download.html"));
    console.log("example-download.html has been downloaded.");
}).then(null, Components.utils.reportError);

But, I keep getting the error below:

enter image description here

So I added the string let {Cu, Ci, CC} = require('chrome') but nothing changes.

I'm 43 years old and I'm still learning JavaScript. I'm aware I don't have the same flexibility I had 2 decades ago. But, I remember that programming was much more straightforward. I still love programming but now I often find it quite frustrating.

Makyen
  • 31,849
  • 12
  • 86
  • 121
Nicero
  • 4,181
  • 6
  • 30
  • 52
  • Please do not put error messages in as an image. Doing so makes it impossible to find the question with a search on any of the strings contained in the error. It also makes it harder for people trying to answer the question to search for information about the error. – Makyen Mar 04 '16 at 13:53
  • If you want to bypass the download manager, you can download it directly with XHR and OS.File - http://stackoverflow.com/questions/25492225/how-to-download-image-to-desktop-with-os-file/25492679#25492679 – Noitidart Mar 06 '16 at 07:04
  • 1
    As well as adding `let {Cu, Ci ...`, you also need to change `Components.utils.` to `Cu.`. For example, this `Components.utils.import("resource://gre/modules/osfile.jsm")`, turns into this `Cu.import("resource://gre/modules/osfile.jsm")` – Kaspar Lee Mar 07 '16 at 10:58

2 Answers2

2

I have not actually tried it, but I would not not expect the destructuring assignment

let {Cu, Ci, Cc} = require('chrome');

[Note: your CC should be Cc.]

to provide your Add-on SDK code to have access to the complete Components object through referencing it as Components, but only to have the properties (sub-objects) which you have assigned to "aliases" be available through the objects that you have defined using let:

Object ("alias") now available           Object full name normally available
to your SDK add-on                       to Overlay and Restartless add-ons
Cu                                =      Components.utils
Ci                                =      Components.interfaces
Cc                                =      Components.classes

The destructuring assignment should have extracted just the properties (sub-objects) referred to as Cu, Ci, and Cc within requre('chrome').

The code you copied from MDN would need to change to:

Cu.import("resource://gre/modules/Downloads.jsm");
Cu.import("resource://gre/modules/osfile.jsm")
Cu.import("resource://gre/modules/Task.jsm");

Task.spawn(function () {
    yield Downloads.fetch("http://www.mozilla.org/",
                          OS.Path.join(OS.Constants.Path.tmpDir,"example-download.html"));
    console.log("example-download.html has been downloaded.");
}).then(null, Cu.reportError);

If you wanted to use Components without using the Cc, Ci, Cu, Cr, and Cm aliases, you would need to use:

let {components} = require('chrome'); // note the lowercase "components"
let Components = components;

With that you could then use your original code:

Components.utils.import("resource://gre/modules/Downloads.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm")
Components.utils.import("resource://gre/modules/Task.jsm");

Task.spawn(function () {
    yield Downloads.fetch("http://www.mozilla.org/",
                          OS.Path.join(OS.Constants.Path.tmpDir,"example-download.html"));
    console.log("example-download.html has been downloaded.");
}).then(null, Components.utils.reportError);

For more information, you can see the Chrome Authority page on MDN.

Makyen
  • 31,849
  • 12
  • 86
  • 121
2

So, I copied the following code from MDN.

This is a big part of your problem. You're copying code without reasoning about it. Code snippets generally have prerequisites and assumptions that must be fulfilled, i.e. they must be evaluated on specific contexts - npm modules won't run in a browser for example.

Those code snippets too have dependencies, e.g. the Components object. The error message warns you about that, so that might be a good hint to read the documentation on require("chrome") and Components.

The second issue is that you're trying to use JSMs in an SDK addon without looking for equivalent SDK APIs first. Note that the top-level MDN Addon page distinguishes several types of extensions, especially SDK and legacy extensions. You're writing an SDK extension.

So for the purpose of downloading images instead of going through the file download manager (Downloads.jsm) you can simply use the request and IO SDK modules to download the file and then child_process to spawn the exe.

Task.spawn(function () {
    yield Downloads.fetch("http://www.mozilla.org/",

That's using yield outside a generator function, which is legacy syntax and should not be used.

For chaining you probably should use ES6 promises instead.

the8472
  • 40,999
  • 5
  • 70
  • 122