function download(url, downloadId) {
        return _extensionManagerCall(function (extensionManager) {
            var d = new $.Deferred();

            // Validate URL
            // TODO: PathUtils fails to parse URLs that are missing the protocol part (e.g. starts immediately with "www...")
            var parsed = PathUtils.parseUrl(url);
            if (!parsed.hostname) {  // means PathUtils failed to parse at all
                d.reject(Errors.MALFORMED_URL);
                return d.promise();
            }
            if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
                d.reject(Errors.UNSUPPORTED_PROTOCOL);
                return d.promise();
            }

            var urlInfo = { url: url, parsed: parsed, filenameHint: parsed.filename };
            githubURLFilter(urlInfo);

            // Decide download destination
            var filename = urlInfo.filenameHint;
            filename = filename.replace(/[^a-zA-Z0-9_\- \(\)\.]/g, "_"); // make sure it's a valid filename
            if (!filename) {  // in case of URL ending in "/"
                filename = "extension.zip";
            }

            // Download the bits (using Node since brackets-shell doesn't support binary file IO)
            var r = extensionManager.downloadFile(downloadId, urlInfo.url, PreferencesManager.get("proxy"));
            r.done(function (result) {
                d.resolve({ localPath: FileUtils.convertWindowsPathToUnixPath(result), filenameHint: urlInfo.filenameHint });
            }).fail(function (err) {
                d.reject(err);
            });

            return d.promise();
        });
    }
Downloads from the given URL to a temporary location. On success, resolves with the path of the
downloaded file (typically in a temp folder) and a hint for the real filename. On failure, rejects
with an error object.

@param {string} url URL of the file to be downloaded
@param {number} downloadId Unique number to identify this request
@return {$.Promise}