将使用 sdk/context-menu API 创建的菜单项添加到上下文菜单的顶部
Add menu item created with the sdk/context-menu API to the top of the context menu
我正在开发一个 Mozilla Add-on 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
}
});
使用附加 SDK sdk/context-menu
API,无法直接将项目添加到上下文菜单底部以外的任何位置。没有方便的方法让它最终出现在顶部。但是,您可以添加它,然后将其移至顶部,或根据需要移至上下文菜单中的其他位置。但是,Add-on SDK 无法让您通过任何 Add-on SDK APIs.
注意:将上下文菜单项移动到菜单顶部会改变 Firefox 附加组件添加的上下文菜单项的正常操作。如果我要加载一个假定其上下文菜单项位于列表顶部的附加组件,我会很不高兴。如果它至少没有提供选项 (simple-prefs) 以在底部或我选择的位置显示上下文菜单项,我很可能会对 AMO 写负面评论。将它放在上下文菜单的顶部假定您的加载项将是该上下文菜单中最常用的选项。这种情况在很大程度上取决于用户、您的附加组件以及用户如何使用您的附加组件。另一方面,如果这是我一直使用的选项,我会很高兴看到它位于顶部,并与其他菜单项分开。
一种使上下文菜单项显示在顶部的方法:
您当前的代码会生成一个上下文菜单,用于在底部添加条目 AddonName(d)
的链接,如下所示:
上下文菜单是 XUL DOM 的一部分,每个 Firefox Window 都存在。 Add-on SDK sdk/context-menu
似乎只允许您将项目添加到页面内容区域内元素的上下文菜单。因此,在 XUL DOM 中,受影响的上下文菜单是具有 id="contentAreaContextMenu"
的 menupopup
:
一旦您的上下文菜单项的 menuitem
存在,您可以操作 XUL DOM 将其移动到 firstChild
,这将把它放在上下文的顶部菜单。但是,SDK 不会将上下文菜单项的 menuitem
插入到 XUL DOM 中,直到它第一次显示(或者至少直到第一次 可能 被显示)。因此,您不能只调用 cm.Item()
然后立即更改 XUL DOM.
更复杂的是,XUL DOM 操作必须发生在您的主代码中,而您只能被告知上下文菜单即将显示(因此您的 menuitem
在来自内容脚本的 XUL DOM)。因此,我们必须侦听 context
事件并将消息传递给我们的主脚本以启动上下文菜单的 XUL DOM 操作。
请记住,let cmItem = cm.Item()
返回的对象实际上并不是对 XUL DOM 中 menuitem
的引用。这样做的原因是 XUL DOM 中的 menuitem
作为单独的对象存在于每个 Firefox window 的 XUL DOM 中。因此,我们必须搜索 XUL DOM 以找到正确的 menuitem
元素。因为它是每个 Firefox window 的单独 XUL DOM,所以我们需要确保它发生在所有 windows 中。在这种情况下,在内容脚本中侦听 context
事件会导致每次显示上下文菜单时都会发生此更改。因此,我们不需要在每个 Firefox 中专门进行更改 window,因为我们会在每次显示上下文菜单时进行更改。
通过 XUL DOM 搜索正确的 menuitem
添加额外的复杂性是附加 SDK 不提供应用的 id
属性到在 XUL DOM 中创建的 menuitem
。因此,我们必须根据 label
属性 的值找到该项目。这意味着您要么不需要更改 属性,要么跟踪更改以便搜索正确的 menuitem
。在下面的示例代码中,假设标签没有改变。
我们现在需要使用从内容脚本传递的消息来指示即将显示上下文菜单的消息,并在单击上下文菜单项时发送 node.href
。为了做到这一点,现在传递的是一个带有 type
属性 的对象,指示传递的消息类型(click
或 context
)和data
来自点击。
注意:附加 SDK 还在附加 SDK 添加的菜单项上方的上下文菜单中添加了一个 menuseparator
。可能是在添加的每个上下文菜单项之间或在每个附加 SDK 扩展添加的所有菜单项之间创建了分隔符。然而,在简短的测试中,即使对于多个附加 SDK 扩展,似乎也只添加了一个分隔符。为了使它看起来干净,您可能希望在输入后将其添加到上下文菜单的顶部。如果您确实添加了一个,则需要确保在您的附加组件被禁用后将其删除。移动此分隔符将假设您是唯一添加到任何上下文菜单的附加 SDK 扩展。我没有在下面的示例代码中操纵这个 menuseparator
。
以下代码会将使用 cm.Item()
创建的单个 menuitem
从上下文菜单中的任何位置移动到上下文菜单的顶部:
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
被简化以使测试更容易(删除了仅显示在某些匹配的 URL 上的限制)。此外,node.href
数据用于 console.log()
,以验证功能。
我正在开发一个 Mozilla Add-on 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
}
});
使用附加 SDK sdk/context-menu
API,无法直接将项目添加到上下文菜单底部以外的任何位置。没有方便的方法让它最终出现在顶部。但是,您可以添加它,然后将其移至顶部,或根据需要移至上下文菜单中的其他位置。但是,Add-on SDK 无法让您通过任何 Add-on SDK APIs.
注意:将上下文菜单项移动到菜单顶部会改变 Firefox 附加组件添加的上下文菜单项的正常操作。如果我要加载一个假定其上下文菜单项位于列表顶部的附加组件,我会很不高兴。如果它至少没有提供选项 (simple-prefs) 以在底部或我选择的位置显示上下文菜单项,我很可能会对 AMO 写负面评论。将它放在上下文菜单的顶部假定您的加载项将是该上下文菜单中最常用的选项。这种情况在很大程度上取决于用户、您的附加组件以及用户如何使用您的附加组件。另一方面,如果这是我一直使用的选项,我会很高兴看到它位于顶部,并与其他菜单项分开。
一种使上下文菜单项显示在顶部的方法:
您当前的代码会生成一个上下文菜单,用于在底部添加条目 AddonName(d)
的链接,如下所示:
上下文菜单是 XUL DOM 的一部分,每个 Firefox Window 都存在。 Add-on SDK sdk/context-menu
似乎只允许您将项目添加到页面内容区域内元素的上下文菜单。因此,在 XUL DOM 中,受影响的上下文菜单是具有 id="contentAreaContextMenu"
的 menupopup
:
一旦您的上下文菜单项的 menuitem
存在,您可以操作 XUL DOM 将其移动到 firstChild
,这将把它放在上下文的顶部菜单。但是,SDK 不会将上下文菜单项的 menuitem
插入到 XUL DOM 中,直到它第一次显示(或者至少直到第一次 可能 被显示)。因此,您不能只调用 cm.Item()
然后立即更改 XUL DOM.
更复杂的是,XUL DOM 操作必须发生在您的主代码中,而您只能被告知上下文菜单即将显示(因此您的 menuitem
在来自内容脚本的 XUL DOM)。因此,我们必须侦听 context
事件并将消息传递给我们的主脚本以启动上下文菜单的 XUL DOM 操作。
请记住,let cmItem = cm.Item()
返回的对象实际上并不是对 XUL DOM 中 menuitem
的引用。这样做的原因是 XUL DOM 中的 menuitem
作为单独的对象存在于每个 Firefox window 的 XUL DOM 中。因此,我们必须搜索 XUL DOM 以找到正确的 menuitem
元素。因为它是每个 Firefox window 的单独 XUL DOM,所以我们需要确保它发生在所有 windows 中。在这种情况下,在内容脚本中侦听 context
事件会导致每次显示上下文菜单时都会发生此更改。因此,我们不需要在每个 Firefox 中专门进行更改 window,因为我们会在每次显示上下文菜单时进行更改。
通过 XUL DOM 搜索正确的 menuitem
添加额外的复杂性是附加 SDK 不提供应用的 id
属性到在 XUL DOM 中创建的 menuitem
。因此,我们必须根据 label
属性 的值找到该项目。这意味着您要么不需要更改 属性,要么跟踪更改以便搜索正确的 menuitem
。在下面的示例代码中,假设标签没有改变。
我们现在需要使用从内容脚本传递的消息来指示即将显示上下文菜单的消息,并在单击上下文菜单项时发送 node.href
。为了做到这一点,现在传递的是一个带有 type
属性 的对象,指示传递的消息类型(click
或 context
)和data
来自点击。
注意:附加 SDK 还在附加 SDK 添加的菜单项上方的上下文菜单中添加了一个 menuseparator
。可能是在添加的每个上下文菜单项之间或在每个附加 SDK 扩展添加的所有菜单项之间创建了分隔符。然而,在简短的测试中,即使对于多个附加 SDK 扩展,似乎也只添加了一个分隔符。为了使它看起来干净,您可能希望在输入后将其添加到上下文菜单的顶部。如果您确实添加了一个,则需要确保在您的附加组件被禁用后将其删除。移动此分隔符将假设您是唯一添加到任何上下文菜单的附加 SDK 扩展。我没有在下面的示例代码中操纵这个 menuseparator
。
以下代码会将使用 cm.Item()
创建的单个 menuitem
从上下文菜单中的任何位置移动到上下文菜单的顶部:
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
被简化以使测试更容易(删除了仅显示在某些匹配的 URL 上的限制)。此外,node.href
数据用于 console.log()
,以验证功能。