如何从 Firefox Add-on SDK 扩展启动正常下载

2023-12-27

我正在为 Firefox 开发附加 SDK 扩展。我发现我需要能够像用户请求一样启动下载,即显示正常的文件保存对话框或将文件保存到用户喜欢的任何位置,因为它可以在首选项>内容下配置。

有关下载的每一篇文章或文档似乎只考虑了我知道在哪里下载文件的情况,但这不是我在这种情况下需要的。在这种情况下,需要就像用户开始下载一样。

如何实现这一点,最好是通过 SDK 的方法?


好吧,你可以启动一个实际的保存。

从您的代码启动保存链接:
在上下文菜单中,命令值为gContextMenu.saveLink();。 saveLink() 定义在:chrome://browser/content/nsContextMenu.js http://dxr.mozilla.org/mozilla-central/source/browser/base/content/nsContextMenu.js#1284。它做一些内务处理然后调用saveHelper() 定义在同一文件中 http://mxr.mozilla.org/mozilla-central/source/browser/base/content/nsContextMenu.js#1150。您可以使用适当的参数调用 saveHelper() 。它包含在面板中chrome://browser/content/web-panels.xul http://dxr.mozilla.org/mozilla-central/source/browser/base/content/web-panels.xul#26 with:

<script type="application/javascript" 
            src="chrome://browser/content/nsContextMenu.js"/>

然后gContextMenu声明于的变量chrome://browser/content/browser.js http://dxr.mozilla.org/mozilla-central/source/browser/base/content/browser.js#45 as null被安排了:
gContextMenu = new nsContextMenu(this, event.shiftKey); http://dxr.mozilla.org/mozilla-central/source/browser/base/content/web-panels.xul#53
在上下文菜单的 onpopupshowing 事件处理程序中。它返回到:
'gContextMenu = null;' http://dxr.mozilla.org/mozilla-central/source/browser/base/content/web-panels.xul#60
in the onpopuphiding事件处理程序。

如果你想在你自己的代码中使用它,你可以这样做:

let urlToSave = "http://stackoverflow.com/questions/26694442";
let linkText = "Some Link text";

//  Add a "/" to un-comment the code appropriate for your add-on type.
/* Overlay and bootstrap:
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
//*/
/* Add-on SDK:
var {Cc, Ci, Cr} = require("chrome");
//*/

if (window === null || typeof window !== "object") {
    //If you do not already have a window reference, you need to obtain one:
    //  Add a "/" to un-comment the code appropriate for your add-on type.
    /* Add-on SDK:
    var window = require('sdk/window/utils').getMostRecentBrowserWindow();
    //*/
    /* Overlay and bootstrap (from almost any context/scope):
    var window=Components.classes["@mozilla.org/appshell/window-mediator;1"]
                         .getService(Components.interfaces.nsIWindowMediator)
                         .getMostRecentWindow("navigator:browser");        
    //*/
}

//Create an object in which we attach nsContextMenu.js.
//  It needs some support properties/functions which
//  nsContextMenu.js assumes are part of its context.
let contextMenuObj = {
    makeURI: function (aURL, aOriginCharset, aBaseURI) {
      var ioService = Cc["@mozilla.org/network/io-service;1"]
                                .getService(Ci.nsIIOService);
      return ioService.newURI(aURL, aOriginCharset, aBaseURI);
    },
    gPrefService: Cc["@mozilla.org/preferences-service;1"]
                            .getService(Ci.nsIPrefService)
                            .QueryInterface(Ci.nsIPrefBranch),
    Cc: Cc,
    Ci: Ci,
    Cr: Cr
};

Cc["@mozilla.org/moz/jssubscript-loader;1"]
        .getService(Ci.mozIJSSubScriptLoader)
        .loadSubScript("chrome://browser/content/nsContextMenu.js"
                       ,contextMenuObj);

//Re-define the initMenu function, as there is not a desire to actually
//  initialize a menu.        
contextMenuObj.nsContextMenu.prototype.initMenu = function() { };

let myContextMenu = new contextMenuObj.nsContextMenu();
//Save the specified URL
myContextMenu.saveHelper(urlToSave,linkText,null,true,window.content.document);

使用 loadSubScript 加载的替代方法nsContextMenu.js:
我的偏好是使用 loadSubScript 加载 saveHelper 代码nsContextMenu.js。这可以使代码与未来 Firefox 版本中所做的任何更改保持同步。但是,它引入了您正在使用来自非官方 API 的函数的依赖性。因此,在未来的 Firefox 版本中,它可能会以某种方式发生变化,并且需要对您的附加组件进行更改。另一种方法是复制saveHelper()您的扩展中的代码。它的定义如下:

// Helper function to wait for appropriate MIME-type headers and
// then prompt the user with a file picker
saveHelper: function(linkURL, linkText, dialogTitle, bypassCache, doc) {
  // canonical def in nsURILoader.h
  const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020;

  // an object to proxy the data through to
  // nsIExternalHelperAppService.doContent, which will wait for the
  // appropriate MIME-type headers and then prompt the user with a
  // file picker
  function saveAsListener() {}
  saveAsListener.prototype = {
    extListener: null, 

    onStartRequest: function saveLinkAs_onStartRequest(aRequest, aContext) {

      // if the timer fired, the error status will have been caused by that,
      // and we'll be restarting in onStopRequest, so no reason to notify
      // the user
      if (aRequest.status == NS_ERROR_SAVE_LINK_AS_TIMEOUT)
        return;

      timer.cancel();

      // some other error occured; notify the user...
      if (!Components.isSuccessCode(aRequest.status)) {
        try {
          const sbs = Cc["@mozilla.org/intl/stringbundle;1"].
                      getService(Ci.nsIStringBundleService);
          const bundle = sbs.createBundle(
                  "chrome://mozapps/locale/downloads/downloads.properties");

          const title = bundle.GetStringFromName("downloadErrorAlertTitle");
          const msg = bundle.GetStringFromName("downloadErrorGeneric");

          const promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"].
                            getService(Ci.nsIPromptService);
          promptSvc.alert(doc.defaultView, title, msg);
        } catch (ex) {}
        return;
      }

      var extHelperAppSvc = 
        Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
        getService(Ci.nsIExternalHelperAppService);
      var channel = aRequest.QueryInterface(Ci.nsIChannel);
      this.extListener = 
        extHelperAppSvc.doContent(channel.contentType, aRequest, 
                                  doc.defaultView, true);
      this.extListener.onStartRequest(aRequest, aContext);
    }, 

    onStopRequest: function saveLinkAs_onStopRequest(aRequest, aContext, 
                                                     aStatusCode) {
      if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
        // do it the old fashioned way, which will pick the best filename
        // it can without waiting.
        saveURL(linkURL, linkText, dialogTitle, bypassCache, false,
                doc.documentURIObject, doc);
      }
      if (this.extListener)
        this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
    },

    onDataAvailable: function saveLinkAs_onDataAvailable(aRequest, aContext,
                                                         aInputStream,
                                                         aOffset, aCount) {
      this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
                                       aOffset, aCount);
    }
  }

  function callbacks() {}
  callbacks.prototype = {
    getInterface: function sLA_callbacks_getInterface(aIID) {
      if (aIID.equals(Ci.nsIAuthPrompt) || aIID.equals(Ci.nsIAuthPrompt2)) {
        // If the channel demands authentication prompt, we must cancel it
        // because the save-as-timer would expire and cancel the channel
        // before we get credentials from user.  Both authentication dialog
        // and save as dialog would appear on the screen as we fall back to
        // the old fashioned way after the timeout.
        timer.cancel();
        channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
      }
      throw Cr.NS_ERROR_NO_INTERFACE;
    } 
  }

  // if it we don't have the headers after a short time, the user 
  // won't have received any feedback from their click.  that's bad.  so
  // we give up waiting for the filename. 
  function timerCallback() {}
  timerCallback.prototype = {
    notify: function sLA_timer_notify(aTimer) {
      channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
      return;
    }
  }

  // set up a channel to do the saving
  var ioService = Cc["@mozilla.org/network/io-service;1"].
                  getService(Ci.nsIIOService);
  var channel = ioService.newChannelFromURI(makeURI(linkURL));
  if (channel instanceof Ci.nsIPrivateBrowsingChannel) {
    let docIsPrivate = PrivateBrowsingUtils.isWindowPrivate(doc.defaultView);
    channel.setPrivate(docIsPrivate);
  }
  channel.notificationCallbacks = new callbacks();

  let flags = Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;

  if (bypassCache)
    flags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;

  if (channel instanceof Ci.nsICachingChannel)
    flags |= Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;

  channel.loadFlags |= flags;

  if (channel instanceof Ci.nsIHttpChannel) {
    channel.referrer = doc.documentURIObject;
    if (channel instanceof Ci.nsIHttpChannelInternal)
      channel.forceAllowThirdPartyCookie = true;
  }

  // fallback to the old way if we don't see the headers quickly 
  var timeToWait = 
    gPrefService.getIntPref("browser.download.saveLinkAsFilenameTimeout");
  var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  timer.initWithCallback(new timerCallback(), timeToWait,
                         timer.TYPE_ONE_SHOT);

  // kick off the channel with our proxy object as the listener
  channel.asyncOpen(new saveAsListener(), null);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从 Firefox Add-on SDK 扩展启动正常下载 的相关文章

  • React js Stripe 结账不起作用

    我正在尝试在 React js 应用程序中呈现条带结账默认表单
  • 每 3 秒重复一次动画

    我正在使用 WOW js 和 animate css 现在我正在将 CSS 运行到 Infinite 我想知道如何让我的课程运行 3 秒停止并再次开始到无限 My html img src images fork png class for
  • 如何重置使用 JavaScript 更改的 CSS 属性?

    我的导航按钮的宽度从 100px 增加到 150px 当鼠标悬停在 nav li hover width 150px 但是使用 javascript 我已经做到了 无论选择哪个选项 宽度都将继续为 150px 当选择每个选项时 它会使其他选
  • 使用 JavaScript 使链接保持活动状态并在单击时显示悬停效果

    I am struggling to make this work I d like to make it where if O F is clicked the hover state stays active if another li
  • Javascript正则表达式用于字母字符和空格? [关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我需要一个
  • JavaScript 重定向到新窗口

    我有以下代码 它根据下拉列表的值重定向到页面 我如何使其在新窗口中打开 function goto form var index form select selectedIndex if form select options index
  • 如何将 Google Charts 与 Vue.js 库一起使用?

    我正在尝试使用 Vue js 库使用 Google Charts 制作图表 但我不知道如何添加到 div 这是我尝试做的 这是如何使用普通 javascript 添加图表 这是文档的代码示例 https developers google
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • 表单计算器脚本基本价格未加载 OnLoad

    我的表单中有一个计算器来计算我的下拉选项选择 function select calculate on change calc input type checkbox calculate on click calc function cal
  • Babel 7 Jest Core JS“TypeError:wks不是函数”

    将我的项目升级到 Babel 7 后 通过 Jest 运行测试会抛出以下错误 测试在 Babel 6 中运行没有任何问题 但在 Babel 7 中失败并出现以下错误 TypeError wks is not a function at Ob
  • 提交表单并重定向页面

    我在 SO 上看到了很多与此相关的其他问题 但没有一个对我有用 我正在尝试提交POST表单 然后将用户重定向到另一个页面 但我无法同时实现这两种情况 我可以获取重定向或帖子 但不能同时获取两者 这是我现在所拥有的
  • Javascript 数组到 VBScript

    我有一个使用 Javascript 构建的对象数组 我需要使用 VBScript 读取它 如下例所示 我找不到在 VbScript 代码中循环遍历数组的方法myArray object 这个例子是我的问题的简化 我无法更改页面的默认语言 这
  • 如何使用tampermonkey模拟react应用程序中的点击?

    我正在尝试使用 Tampermonkey 脚本模拟对 React 元素的点击 不幸的是 由于 React 有自己的影子 DOM 所以天真的方法使用document querySelector 不工作 我遇到了一些需要修改 React 组件本
  • Laravel 中只向登录用户显示按钮

    如果我以 John 身份登录 如何才能只显示 John 的红色按钮而不显示 Susan 的红色按钮 测试系统环境 Win10 Laravel5 4 Mysql5 7 19 table class table table responsive
  • 为 illustrator 导出脚本以保存为 web jpg

    任何人都可以帮我为 illustrator CC2017 编写一个脚本 将文件以 JPG 格式导出到网络 旧版 然后保存文件并关闭 我有 700 个文件 每个文件有 2 个画板 单击 文件 gt 导出 gt 另存为 Web 旧版 然后右键文
  • 为什么我不能在 AngularJS 中使用 data-* 作为指令的属性名称?

    On the t他的笨蛋 http plnkr co edit l3KoY3 p preview您可以注意到属性名称模式的奇怪行为data 在指令中 电话 Test of data named attribute br
  • Javascript 纪元时间(以天为单位)

    我需要以天为单位的纪元时间 迄今为止 我已经看到过有关如何翻译它的帖子 但几天后就没有了 我对纪元时间很不好 我怎么能得到这个 我需要以天为单位的纪元时间 我将解释为您想要自纪元以来的天数 纪元本身是第 0 天 或第 1 天的开始 无论您如
  • JQuery 图像上传不适用于未来的活动

    我希望我的用户可以通过帖子上传图像 因此 每个回复表单都有一个上传表单 用户可以通过单击上传按钮上传图像 然后单击提交来提交帖子 现在我的上传表单可以上传第一个回复的图像 但第二个回复的上传不起作用 我的提交过程 Ajax 在 php 提交
  • 将 MQTTNet 服务器与 MQTT.js 客户端结合使用

    我已经启动了一个 MQTT 服务器 就像this https github com chkr1011 MQTTnet tree master例子 该代码托管在 ASP Net Core 2 0 应用程序中 但我尝试过控制台应用程序 但没有成
  • fullCalendar 未显示正确的结束日期

    我正在看调试页面 http jsbin com wukofacaxu edit js outputFullCalendar 官方网站的 我想安排一个活动时间为 22 09 2015 至 30 09 2015 dd mm yyyy 但它只显示

随机推荐

  • python:带有字符串输入的调度方法

    我需要编写一个接受 3 个参数的方法 a string带有函数名称 一个有序的list该函数的参数 这包括具有默认值的参数和 varargs 但不包括 kwargs a dict表示任何附加关键字参数 或None如果没有 我需要使用此输入来
  • android-opencv 使用 matToBitmap/bitmapToMat 将 mat 转换为灰度

    我在 eclipse 中使用更新的 willowgarage opencv 库 我想将 mat 变量转换为灰度 我已经尝试了在网上找到的所有内容 但它们对我不起作用 这是我的代码 package com deneme deneme impo
  • 获取 Java 时区的夏令时转换日期

    我想知道在 Java 中最简单的方法来获取未来夏令时将发生变化的日期列表 一种相当不优雅的方法是简单地迭代多年的日子 并根据 TimeZone inDaylightTime 测试它们 这会起作用 而且我不担心效率 因为这只需要在每次我的应用
  • 我应该在 C# 项目中使用 WPF 还是 Windows 窗体应用程序?

    我正在开发一个基于客户端 服务器的应用程序 其中客户端应用程序将访问服务器数据库来存储计费信息 它还将具有报告生成功能 Windows 窗体在文档打印方面表现出色 但我在 WPF 中没有看到这样的功能或控件 如果我错了 请纠正我 我想要数据
  • &pointer 如何具有指向指针的类型?

    struct node int a int main struct node y 23 struct node x y return 0 这是我遇到的一些代码 我弄乱了代码并发现 x 有类型指针到指针 我很困惑这是怎么回事 所以我把它画出来
  • 如何从grails中的控制器调用服务

    我有一个服务类 我试图在我的控制器中调用该服务的方法 如下所示 class LogListController def ListLogDetails println We are inside List log Details gt par
  • AWS EventBridge - 读取事件档案

    有谁知道是否有一个 API 可以读取使用 EventBridge 归档功能归档的事件 我们的目标是进行事件重播 但开箱即用的事件重播功能对我们不起作用 因为我们需要保留事件的时间顺序 作为一种解决方法 我想知道是否有一个选项可以通过拖网事件
  • RxJava 在多个订阅者之间共享 Observable 的排放

    我有以下问题 我有一个可观察量正在做一些工作 但其他可观察量需要该可观察量的输出才能工作 我曾尝试多次订阅同一个可观察量 但在日志中我看到原始可观察量已启动多次 这就是我的观察结果 即创建对象 Observable create Obser
  • 仅当我激活工作表时,VBA 复制和粘贴才有效

    我正在工作表之间复制一些范围 但我不知道为什么只有在复制或粘贴工作表之前激活工作表时它才有效 这有效 s Activate s Range Cells 2 8 Cells lrow 8 Copy d Activate d Range Cel
  • Javascript 解析/评估顺序?

    这可能是一个棘手的问题 但我不明白为什么会这样 这会发出警报 function foo 但我希望在定义函数 foo 之前评估警报 有人可以解释我对解析 评估顺序的不理解 或者指出我不理解的资源吗 JavaScript 与 PHP 一样 跟踪
  • null 或empty 的更简单写法?

    我确信我在这里错过了一些东西 对于某个项目 我需要检查字符串是否为空或为空 有没有更简单的方法来写这个 if myString myString null 是的 有String IsNullOrEmpty https msdn micros
  • 字符串连接可以用于包含 SpEL 的应用程序 yml 值吗?

    我正在尝试定义一个 Spring 数据源 url 如下所示 spring datasource url jdbc vcap services compose for mysql credentials uri useSSL true req
  • 在 Rust 中逐行读取大文件[重复]

    这个问题在这里已经有答案了 我的 Rust 程序旨在逐行读取非常大 最多几 GB 的简单文本文件 问题是 这个文件太大 无法一次读取 或者将所有行传输到一个Vec
  • IntelliJ 自动完成替换函数名称

    我已经从 Eclipse 切换到 IntelliJ 但有一些东西我还没有找到 也没有在 google 上找到 How to get the autocomplete to replace the name of the function I
  • 无法销毁 Firebase 连接,导致热 Lambda 由于“Firebase 应用程序名称‘[DEFAULT]’已存在”而失败

    几个小时以来我一直在尝试我能想到的每一种方法 基本上 我正在运行一个 AWS Lambda 函数 它以客户端和服务器角色对我的 Firebase 应用程序执行一些工作 在 Lambda 上 我需要能够逆转firebase initializ
  • 多边形分解——去除凹点形成凸多边形

    我想解构以下以蓝色显示的多边形 从多边形中删除导致凹面的所有点 目前 我一直在尝试做的是 将每个点从多边形中取出 测试该点以查看它是否落在由该集合的其余部分创建的多边形内 如果为 true 则删除该点 如果为假 请保留要点 这在大多数情况下
  • 以“Managed”结尾的类名是什么意思 (C# .NET)?

    我对 C 比较陌生 所以请耐心等待 我了解托管代码和非托管代码之间的基本区别 但我仍然有点困惑何时使用某些方法 例如 某些类名称结尾中的 托管 一词意味着什么 这是否意味着他们受到管理 而其他所有人员则不受管理 例如 两者之间有什么区别Ae
  • iOS 12 iPad 拒绝启动请求 - Xcode

    直到昨天 我已经更新一个应用程序 5 年多了 没有出现任何问题 我将 iPad 更新到了 iOS 12 但是每次尝试运行它时 我都会收到以下消息 iPad 拒绝了发布请求 我在其他装有 iOS 11 的物理设备和模拟器上进行了测试 只有 i
  • 在 Ext.data 上下文中,JsonStore 和 JsonReader 之间的基本区别是什么?

    在 Ext data 上下文中 JsonStore 和 JsonReader 之间的基本区别是什么 我的意思是 当我应该使用 JsonStore 和当我应该使用 JsonReader 时 两者都提供相同的解决方案 实际上它们是两个不同的东西
  • 如何从 Firefox Add-on SDK 扩展启动正常下载

    我正在为 Firefox 开发附加 SDK 扩展 我发现我需要能够像用户请求一样启动下载 即显示正常的文件保存对话框或将文件保存到用户喜欢的任何位置 因为它可以在首选项 gt 内容下配置 有关下载的每一篇文章或文档似乎只考虑了我知道在哪里下