【问题标题】:Execute script on 'onInstalled' in Firefox Add-on SDK extension在 Firefox 附加 SDK 扩展中的“onInstalled”上执行脚本
【发布时间】:2020-01-18 05:47:29
【问题描述】:

我对 Mozilla 扩展开发非常陌生,即使我刚刚了解扩展和插件开发也是不同的 而且我对我在 MDN(Mozilla 开发者网络)上看到的内容非常坚持。 我想在我的插件安装后立即执行脚本'content_script.js',这样用户就不需要重新启动浏览器。

我只是拖放我的 xpi 文件并安装它,然后我单击网页上的一个按钮,该按钮向我的插件发送消息,但我的插件仅在我重新加载网页后才会收听此消息。

//main.js

       var chrome = require("chrome");      
    chrome.runtime.onInstalled.addListener(function(){
             executeScript (contentscript.js) in tabs});

//also I tried
    browser.runtime.onInstalled.addListener

当我知道它适用于 Mozilla 扩展而不是附加组件时,我正在尝试这个,因为它给了我错误 browser undefined 和 chrome.runtime is undefined。

然后,我在AddonManager.addAddonListener() 中找到了onInstalled()

但是,我仍然很困惑如何在我的插件中使用它。

无论我尝试什么方式都会给我错误。

//main.js
    const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
    Cu.import("resource://gre/modules/AddonManager.jsm");


    AddonManager.addAddonListener.onInstalled.addListener(function(){
        console.log('installed');
        tabs.executeScript(tabId, "../data/content_script.js", function(result) { console.log('result ='+result); });           
    });

以下代码删除了错误,但也没有工作:

    const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
    Cu.import("resource://gre/modules/AddonManager.jsm");


    var listener = {
  onInstalled: function(addon) {
      console.log('installed');
      console.log(addon); 
        tabs.executeScript(tabId, "../data/content_script.js", function(result) { console.log('result ='+result); });           
  }
 };

     AddonManager.addAddonListener(listener);

我的 package.json 看起来像这样

    {
  "title": "test",
  "name": "test",
  "version": "1.0.0",
  "description": "test addon corp",
  "main": "lib/main.js",
  "author": "test",
  "engines": {
    "firefox": ">=38.0a1",
    "fennec": ">=38.0a1"
  },
  "license": "MIT",
  "keywords": [
    "jetpack"
  ]
}

main 包含 main.js,但它仍然没有执行,因为当我重新加载页面时,我的插件才起作用。

我认为我做错了,我是在 Chrome 扩展程序中做的,这很容易,但我对 Mozilla 插件一无所知。我什至无法达到“已安装”。执行 content_script 很远。

【问题讨论】:

  • 您似乎混淆了各种不同的 Mozilla 附加组件类型(特别是四种不同类型的扩展。我建议您阅读 Docs:Introduction to Firefox add-ons:IntroductionAdd-ons on MDN。希望这些将有助于清除一些混乱。
  • 您需要明确选择是否要编写基于WebExtensionsAdd-on SDK 的扩展。完成您选择的示例/教程/教程。对于这两种类型,您都不需要做任何特别的事情来让您的后台脚本在安装后立即运行。如果您这样做了,您用于添加侦听器以安装事件的代码将如何运行以设置侦听器?该代码在添加侦听器时已经在运行。
  • 感谢@Makyen 的回复,我正在开发附加 SDK,我需要从安装它的那一刻起使我的附加组件适用,但它还没有发生,它只有在页面重新加载。
  • @Noitidart,虽然在 WebExtensions 中,runtime.onInstalled 尚不支持,但 OP 也在尝试使用 AddonManager onInstalled 事件。支持 AddonManager onInstalled 事件。当您说尚不支持时,在您的评论文本中更具体一点(而不是最后的链接)可能会有所帮助。 OP 已经在 WebExtensions 和 Add-on SDK 之间混淆了,没有具体说明无助于澄清这一点。

标签: javascript firefox-addon firefox-addon-sdk


【解决方案1】:

您似乎遇到了一些问题:

  • 您仍在混合使用 WebExtensions 和附加 SDK
  • 用于安装当前正在安装的插件的AddonManageronInstalled 事件发生在运行插件的任何代码之前。因此,您将不会收到正在侦听的插件的此事件。
    • AddonManager 主要用于让您的插件能够监控涉及其他 插件的活动。它在监控执行监控的插件活动方面的用处有限。
    • 如果用户在删除插件之前禁用它,您可以获得AddonManager onDisabled 事件。如果用户直接删除它,你只会得到一个onUninstalling 事件。
  • 您可能会遇到以下问题:内容脚本中的console.log() 未显示在浏览器控制台中,当内容脚本由作为主要加载项加载的加载项附加时,正在测试jpm run。输出在执行jpm run 的控制台窗口中可用。虽然输出进入控制台窗口是设计使然,但我认为输出没有进入浏览器控制台(除了控制台窗口之外)是一个错误或错误功能。
  • 将内容脚本加载到about:* 页面会导致一些与将其加载到普通网页不同的行为。此行为可能包括使console.log() 输出仅显示为好像它是使用dump() 输出的一样(即它不会显示在浏览器控制台中,但会显示在执行 Firefox/jpm run 的控制台中)。如果您尝试这样做,则需要尝试您能做的事情。
  • 您曾说过,“我认为我的 main.js 在安装后没有立即运行。”这种信念可能是由于没有在正确的位置寻找console.log() 输出,Browser Console。因此,我有下面的附加组件会自动打开浏览器控制台。
    • 在安装插件时运行package.json"main" 中指定的JavaScript 文件。它也会在 Firefox 启动时运行。
    • 您可以拥有一个exports.main 函数,在评估并执行您的"main" JavaScript 文件中的代码后自动调用该函数。此功能可用于查找执行附加组件的原因。可能的原因有installenablestartupupgradedowngrade。下面的插件演示了何时执行此代码并将reason 传递给它。

为了展示各种各样的东西,我编写了一个 Firefox Add-on SDK 扩展,它既可以加载几个内容脚本,又可以监听 AddonManager 事件。我制作了这个附加组件的两个版本,它们的不同之处仅在于在各自的 package.json 文件中分配给每个版本的名称和 ID。这两个附加组件是installinfo@ex1installinfo@ex2

第一个附加组件installinfo@ex1 是通过在其目录中运行jpm run 来加载的。第二个附加组件installinfo@ex2 是通过拖放jpm xpi 为其创建的.xpi 来安装的。使用 Firefox UI,我立即导航到 about:addons (Ctrl-Shift-A, Cmd-Shift-A 在 OSX 上)并继续首先禁用 installinfo@ex2;然后“删除”installinfo@ex2;然后刷新about:addons 页面,使其无法“撤消”删除。然后我退出 Firefox 主浏览器窗口。

附加组件有大量输出到控制台,因此您可以查看事件发生的顺序以及哪个附加组件能够接收哪些AddonManager 事件。控制台输出[括号中是我对用户界面和一些 cmets 所做的事情]:

[User action: Start Firefox in installinfo@ex1 directory]
    installinfo@ex1: In index.js
    installinfo@ex1: In installAddonListener: Adding add-on listener
    installinfo@ex1: Attaching content script A
    installinfo@ex1: In exports.main: This add-on is being loaded for reason= install Object { loadReason: "install", staticArgs: undefined }
    installinfo@ex1: Attaching content script B
    installinfo@ex1: In exports.main: was passed callbacks= Object { print: print(), quit: function () }
[Note: no console.log output from within conentScriptA loading]
    installinfo@ex1: received message from contentScriptA: Is Loaded
[Note: no console.log output from within conentScriptB loading]
    installinfo@ex1: received message from contentScriptB: Is Loaded
[User action: Drag and drop .xpi for installinfo@ex2 onto Firefox]
    installinfo@ex1: AddonManager Event: Installing addon ID: installinfo@ex2 ::needsRestart= false ::addon object: Object {  }
    installinfo@ex1: AddonManager Event: Installed addon ID: installinfo@ex2 ::addon object: Object {  }
            installinfo@ex2: In index.js
            installinfo@ex2: In installAddonListener: Adding add-on listener
            installinfo@ex2: Attaching content script A
            installinfo@ex2: In exports.main: This add-on is being loaded for reason= install Object { loadReason: "install", staticArgs: undefined }
            installinfo@ex2: Attaching content script B
            installinfo@ex2: In exports.main: was passed callbacks= Object { print: print(_), quit: function () }
                installinfo@ex2: In contentScriptA: Loaded
            installinfo@ex2: received message from contentScriptA: Is Loaded
                installinfo@ex2: In contentScriptB: Loaded
            installinfo@ex2: received message from contentScriptB: Is Loaded
[User action: Navigate to about:addons]
[User action: Disable installinfo@ex2]
    installinfo@ex1: AddonManager Event: Disabling addon ID: installinfo@ex2 ::needsRestart= false ::addon object: Object {  }
            installinfo@ex2: AddonManager Event: Disabling addon ID: installinfo@ex2 ::needsRestart= false ::addon object: Object {  }
            installinfo@ex2: In exports.onUnload: This add-on is being unloaded for reason= disable
            installinfo@ex2: In removeAddonListener: Removing add-on listener
    installinfo@ex1: AddonManager Event: Disabled addon ID: installinfo@ex2 ::addon object: Object {  }
            installinfo@ex2: AddonManager Event: Disabled addon ID: installinfo@ex2 ::addon object: Object {  }
    installinfo@ex1: AddonManager Event: Uninstalling addon ID: installinfo@ex2 ::needsRestart= true ::addon object: Object {  }
[Get a warning in Browser Console because installinfo@ex2 did not remove its AddonManager listeners, and AddonManager is still trying to call them.]
            1472563865661   addons.manager   WARN   AddonListener threw exception when calling onUninstalling: TypeError: can't access dead object (resource://gre/modules/AddonManager.jsm:1756:1) JS Stack trace: AddonManagerInternal.callAddonListeners@AddonManager.jsm:1756:1 < this.AddonManagerPrivate.callAddonListeners@AddonManager.jsm:3075:5 < this.XPIProvider.uninstallAddon@XPIProvider.jsm:5041:7 < AddonWrapper.prototype.uninstall@XPIProvider.jsm:7484:5 < uninstall@extensions.xml:1548:13 < oncommand@about:addons:1:1
[User action: Refresh about:addons page to remove "undo" posibility for installinfo@ex2]
    installinfo@ex1: Uninstalled addon ID: installinfo@ex2 ::addon object: Object {  }
[Get a warning in Browser Console because installinfo@ex2 did not remove its AddonManager listeners, and AddonManager is still trying to call them.]
            1472563873408   addons.manager   WARN   AddonListener threw exception when calling onUninstalled: TypeError: can't access dead object (resource://gre/modules/AddonManager.jsm:1756:1) JS Stack trace: AddonManagerInternal.callAddonListeners@AddonManager.jsm:1756:1 < this.AddonManagerPrivate.callAddonListeners@AddonManager.jsm:3075:5 < this.XPIProvider.uninstallAddon@XPIProvider.jsm:5096:7 < AddonWrapper.prototype.uninstall@XPIProvider.jsm:7484:5 < doPendingUninstalls@extensions.js:1740:5 < gListView.hide@extensions.js:2733:5 < gViewController.shutdown@extensions.js:651:7 < shutdown@extensions.js:184:3 < EventListener.handleEvent*@extensions.js:84:1
[User action: Close main Firefox browser window]
[User action: Close Firefox Browser Console window]
    (via dump):installinfo@ex1: In exports.onUnload: This add-on is being unloaded for reason= shutdown

我建议您对下面的插件进行试验,以了解 AddonManager 事件的可能性以及代码在插件中的执行时间。

附加文件:

index.js

/* Firefox Add-on SDK test when code is executed upon install*/

//For testing:
var doNotRemoveAddonManagerListenersUponUninstall = true

var self = require("sdk/self");
var tabs = require("sdk/tabs");
var myId = self.id;
let myIdText = myId;
if(myId.indexOf('2') > -1){
    //Indent console logs for version 2
    myIdText = '\t\t' + myIdText ;
}

var utils = require('sdk/window/utils');
activeWin = utils.getMostRecentBrowserWindow();
//This will execute every time the add-on is loaded (e.g. install, startup, enable, etc).
myLog('In index.js');

//Open the Browser Console
activeWin.document.getElementById('menu_browserConsole').doCommand();

function myLog(){
    // Using activeWin.console.log() is needed to have output to the
    // Browser Console when installed as an .xpi file.  In that case,
    // console is mapped to dump(), which does not output to the console. 
    // This is done, I assume, to not polute the console from SDK add-ons
    // that are logging information when they should not.  Using jpm run,
    // console.log outputs to the Browser Console.
    let activeWin = require('sdk/window/utils').getMostRecentBrowserWindow();
    // If Firefox is exiting (and some other conditions), there is
    // no active window.  At such times, we must use the version
    // of console.log() aliases to dump().
    let useConsole = console;
    if(activeWin ){
        //useConsole = activeWin.console;
    }
    useConsole.log(myIdText +':',...arguments);
}

function attachScript(script){
    let tabWorker = tabs.activeTab.attach({
        contentScriptFile: script,
        contentScriptOptions: {
            //extra \t because content script console.log (from .xpi) doesn't prefix with
            //  add-on name.
            idText: '\t\t\t' + myIdText
        }
    });
    tabWorker.port.on('consoleLog',logMessage);
    return tabWorker;
}

function logMessage(message){
    myLog(message);
}

exports.main = function (options,callbacks) {
    myLog('In exports.main: This add-on is being loaded for reason=', options.loadReason
          , options);
    myLog('Attaching content script B');
    attachScript('./contentScriptB.js');
    if(typeof callbacks !== 'undefined'){
        myLog('In exports.main: was passed callbacks=', callbacks);
    }
    switch (options.loadReason) {
        case 'install':
            //Do actions upon install
            break;
        case 'enable':
            //Do actions upon enable
            break;
        case 'startup':
            //Do actions upon startup
            break;
        case 'upgrade':
            //Do actions upon upgrade
            break;
        case 'downgrade':
            //Do actions upon downgrade
            break;
        default:
            throw 'Unknown load reason:' + options.loadReason;
            break; //Not needed, throw will exit
    }
};

exports.onUnload = function (reason) {
    myLog('In exports.onUnload: This add-on is being unloaded for reason=',reason);
    //Your add-on listeners are NOT automatically removed when
    //  your add-on is disabled/uninstalled. 
    //You MUST remove them in exports.onUnload if the reason is
    //  not 'shutdown'.  If you don't, errors will be shown in the
    //  console for all events for which you registered a listener.
    if(reason !== 'shutdown') {
        uninstallAddonListener();
    }
};


const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");

let addonListener = {
    onEnabling: function(addon, needsRestart){
        myLog('AddonManager Event: Enabliling addon ID: ' + addon.id 
              + ' ::needsRestart= ' + needsRestart + ' ::addon object:', addon);
    },
    onEnabled: function(addon){
        myLog('AddonManager Event: Enabled addon ID: ' + addon.id 
              + ' ::addon object:', addon);
    },
    onDisabling: function(addon, needsRestart){
        myLog('AddonManager Event: Disabling addon ID: ' + addon.id 
              + ' ::needsRestart= ' + needsRestart + ' ::addon object:', addon);
    },
    onDisabled: function(addon){
        myLog('AddonManager Event: Disabled addon ID: ' + addon.id 
              + ' ::addon object:', addon);
    },
    onInstalling: function(addon, needsRestart){
        myLog('AddonManager Event: Installing addon ID: ' + addon.id 
              + ' ::needsRestart= ' + needsRestart + ' ::addon object:', addon);
    },
    onInstalled: function(addon){
        myLog('AddonManager Event: Installed addon ID: ' + addon.id 
              + ' ::addon object:', addon);
    },
    onUninstalling: function(addon, needsRestart){
        myLog('AddonManager Event: Uninstalling addon ID: ' + addon.id 
              + ' ::needsRestart= ' + needsRestart + ' ::addon object:', addon);
    },
    onUninstalled: function(addon){
        myLog('AddonManager Event: Uninstalled addon ID: ' + addon.id 
              + ' ::addon object:', addon);
    },
    onOperationCancelled: function(addon){
        myLog('AddonManager Event: Add-on Operation Canceled addon ID: ' 
              + addon.id + ' ::addon object:', addon);
    },
    onPropertyChanged: function(addon, properties){
        myLog('AddonManager Event: Add-on Property Changed addon ID: ' + addon.id 
              + ' ::properties= ', properties, ' ::addon object:', addon);
    }
}



function installAddonListener(){
    //Using an AddonManager listener is not effective to listen for your own add-on's
    //  install event.  The event happens prior to you adding the listener.
    myLog('In installAddonListener: Adding add-on listener');
    AddonManager.addAddonListener(addonListener);
}

function uninstallAddonListener(){
    if(doNotRemoveAddonManagerListenersUponUninstall === true){
        //Do the WRONG thing to demonstrate what happens when you don't
        //  remove AddonManager listeners upon your add-on being disabled.
        return;
    }
    //Using an AddonManager listener is not effective to listen for your own add-on's
    //  install event.  The event happens prior to you adding the listener.
    myLog('In removeAddonListener: Removing add-on listener');
    AddonManager.removeAddonListener(addonListener);
}

installAddonListener();

myLog('Attaching content script A');
attachScript('./contentScriptA.js');

数据/内容脚本A

//console.log is ailiased to dump() when running under jpm run. Thus,
//  you will not see the output from this line in the Browser Console
//  when run under jpm run.  It will appear in the console window from 
//  which you executed 'jpm run'
//  From an .xpi it outputs to the Browser Console, as expected.
console.log(self.options.idText + ': In contentScriptA: Loaded');

//Send a message to the background script that this content script is loaded.
self.port.emit('consoleLog', 'received message from contentScriptA: Is Loaded');

数据/内容脚本B

//console.log is ailiased to dump() when running under jpm run. Thus,
//  you will not see the output from this line in the Browser Console
//  when run under jpm run.  It will appear in the console window from 
//  which you executed 'jpm run'
//  From an .xpi it outputs to the Browser Console, as expected.
console.log(self.options.idText + ': In contentScriptB: Loaded');

//Send a message to the background script that this content script is loaded.
self.port.emit('consoleLog', 'received message from contentScriptB: Is Loaded');

package.json(用于 installinfo@ex1):

{
    "title": "Demo when code executes re install 1",
    "name": "installinfo1",
    "id": "installinfo@ex1",
    "version": "0.0.1",
    "description": "Demo when execute various code with respect to install",
    "main": "index.js",
    "author": "Makyen",
    "engines": {
        "firefox": ">=38.0a1",
        "fennec": ">=38.0a1"
    },
    "license": "MIT",
    "keywords": [
        "jetpack"
    ]
}

package.json(用于 installinfo@ex2):

{
    "title": "Demo when code executes re install 2",
    "name": "installinfo2",
    "id": "installinfo@ex2",
    "version": "0.0.1",
    "description": "Demo when execute various code with respect to install",
    "main": "index.js",
    "author": "Makyen",
    "engines": {
        "firefox": ">=38.0a1",
        "fennec": ">=38.0a1"
    },
    "license": "MIT",
    "keywords": [
        "jetpack"
    ]
}

【讨论】:

  • 深深深深!谢谢大哥!
  • 嘿@Makyen 真的很好,你解释得这么详细.. 非常感谢你的努力。它帮助我了解我必须做什么。再次感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-23
  • 1970-01-01
  • 2018-01-17
  • 2012-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多