将使用sdk / context-menu API创建的菜单项添加到上下文菜单的顶部

时间:2016-06-12 04:36:36

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

我正在使用Mozilla附加SDK扩展,该扩展使用sdk/context-menu API提供右键单击上下文菜单选项,并在特定网站上执行某些操作。除了将上下文菜单项添加到列表的底部之外,一切正常。我希望它是上下文菜单中的第一个项目。我搜索了一会儿该怎么做,但没有运气。

以下是我用来创建上下文菜单项的代码(经过修改以混淆使用它的网站):

var cm = require("sdk/context-menu");
cm.Item({
   label: "AddonName(d)",
   context: [
     cm.URLContext(["*.somesite.com"]),
     cm.SelectorContext("a[href]")
   ],
   contentScript: 'self.on("click", function (node, data) {' +
               '  self.postMessage(node.href);' +
               '});',
   accessKey: "d",
   onMessage: function (src) {
       //code
     }
});

1 个答案:

答案 0 :(得分:2)

使用附加SDK sdk/context-menu API,无法直接将项目添加到上下文菜单底部的任何位置。没有方便的方法让它最终在顶部。但是,如果需要,您可以添加它,然后将其移动到顶部或上下文菜单中的其他位置。但是,Add-on SDK无法通过任何附加SDK API实现此目的。

注意:将上下文菜单项移动到菜单顶部会更改Firefox附加组件添加的上下文菜单项的正常操作。如果我要加载一个假设其上下文菜单项属于列表顶部的附加组件,我将不会高兴。如果它没有,至少提供选项(simple-prefs)以在底部或我选择的地方设置上下文菜单项,我可能会很好地在AMO上写一篇负面评论。将其放在上下文菜单的顶部会假定您的加载项将是该上下文菜单中最常用的选项。这种情况将在很大程度上取决于用户,您的插件以及用户如何使用您的插件。另一方面,如果这是我一直使用的选项,我会很高兴看到它在顶部,并与其他菜单项分开。

让您的上下文菜单项显示在顶部的一种方法:

您当前的代码会生成一个上下文菜单,其中包含添加条目AddonName(d)的链接,该条目位于底部,如下所示:
original look of link context menu after addition on bottom

上下文菜单是每个Firefox窗口都存在的XUL DOM的一部分。加载项SDK sdk/context-menu似乎只允许您将项目添加到页面内容区域内元素的上下文菜单中。因此,在XUL DOM中,受影响的上下文菜单是具有menupopup的{​​{1}}: enter image description here

一旦您的上下文菜单项的id="contentAreaContextMenu"存在,您就可以操纵XUL DOM将其移动到menuitem,它将把它放在上下文菜单的顶部。但是,SDK不会将您的上下文菜单项的firstChild插入到XUL DOM中,直到它第一次显示(或者至少直到第一次它可能为止显示)。因此,您不能只调用menuitem然后立即更改XUL DOM。

更复杂的是,XUL DOM操作必须在主代码中进行,而您只能被告知上下文菜单即将显示(因此您的cm.Item()在XUL DOM中)来自内容脚本。因此,我们必须监听menuitem事件并将消息传递给我们的主脚本以启动上下文菜单的XUL DOM操作。

请记住,context返回的对象实际上并不是对XUL DOM中let cmItem = cm.Item()的引用。原因是XUL DOM中的menuitem作为每个Firefox窗口的XUL DOM中的单独对象存在。因此,我们必须搜索XUL DOM以找到正确的menuitem元素。因为它是每个Firefox窗口的单独XUL DOM,我们需要确保它在所有窗口中都发生。在这种情况下,在内容脚本中侦听menuitem事件会导致每次显示上下文菜单时发生此更改。因此,我们不需要在每个Firefox窗口中专门进行更改,因为我们每次都会显示上下文菜单。

在XUL DOM中搜索正确的context时,添加额外的复杂功能是附加组件SDK不提供menuitem属性,该属性应用于在{0}中创建的id XUL DOM。因此,我们必须根据menuitem属性的值找到该项。这意味着您要么不需要更改该属性,要么跟踪更改,以便您搜索正确的label。在下面的示例代码中,假设标签不会更改。

我们现在需要使用从内容脚本传递的消息来指示将要显示上下文菜单的两个消息,并在单击上下文菜单项时发送menuitem。为了做到这一点,传递的内容现在是一个对象,其node.href属性指示传递的消息类型(typeclick)和{{1}点击。

注意:Add-on SDK还在Add-on SDK添加的菜单项上方的上下文菜单中添加了menuseparator。可能是在添加的每个上下文菜单项之间或在每个Add-on SDK扩展添加的所有上下文菜单项之间创建了分隔符。但是,在简短的测试中,似乎只添加了一个分隔符,即使对于多个附加SDK扩展也是如此。为了使这看起来干净,许多人想要在输入后在上下文菜单的顶部添加一个。如果您确实添加了一个,则需要确保在禁用加载项时将其删除。移动此分隔符将假设您是添加到任何上下文菜单的唯一Add-on SDK扩展。我没有在下面的示例代码中操作此context

以下代码会将使用data创建的单menuseparator从上下文菜单中的任何位置移动到上下文菜单的顶部:

menuitem

以上代码会生成链接的上下文菜单,如下所示:
context menu moved to top

注意:对问题中的代码进行了一些更改以进行测试或验证功能。简化了cm.Item()以简化测试(删除限制仅显示在某些匹配的URL上)。此外,let cm = require("sdk/context-menu"); let cmLabel = "AddonName(d)"; cm.Item({ label: cmLabel, context: [ cm.SelectorContext("a[href]") ], contentScript: 'self.on("click", function (node, data) {' + ' self.postMessage({type:"click", data:node.href});' + '});' + 'self.on("context", function (node) {' + ' self.postMessage({type:"context"});' + ' return true;' + '});', accessKey: "d", onMessage: function (message) { if(message.type === "click") { console.log("context menu selected on:" + message.data); } else if (message.type === "context") { adjustContextMenuOrder(cmLabel); } } }); function adjustContextMenuOrder(label) { curWindow = require('sdk/window/utils').getMostRecentBrowserWindow(); let contextMenulist = curWindow.document.getElementById("contentAreaContextMenu"); //Get a list of elements with a matching label property. let itemList = contextMenulist.querySelectorAll('[label="' + label + '"]'); menuitem = itemList[0]; //Assume the first one found is the one we want. if(menuitem === undefined) { return; //Did not find the menuitem. } let parent = menuitem.parentNode; parent.insertBefore(menuitem, parent.firstChild); //Move found element to the top. } 数据用于context,以验证功能。