chrome菜单
Some checks failed
test / Run tests (push) Failing after 3s
build / Build (push) Failing after 5s

This commit is contained in:
2025-04-07 22:20:22 +08:00
parent c43afb0a94
commit f26aecd10f
7 changed files with 164 additions and 19 deletions

View File

@ -86,13 +86,19 @@ export class ExtensionMessageConnect implements MessageConnect {
}
export class ExtensionContentMessageSend extends ExtensionMessageSend {
constructor(private tabId: number) {
constructor(
private tabId: number,
private options?: {
frameId?: number;
documentId?: string;
}
) {
super();
}
sendMessage(data: any): Promise<any> {
return new Promise((resolve) => {
chrome.tabs.sendMessage(this.tabId, data, (resp) => {
chrome.tabs.sendMessage(this.tabId, data, this.options || {}, (resp) => {
resolve(resp);
});
});
@ -100,7 +106,7 @@ export class ExtensionContentMessageSend extends ExtensionMessageSend {
connect(data: any): Promise<MessageConnect> {
return new Promise((resolve) => {
const con = chrome.tabs.connect(this.tabId);
const con = chrome.tabs.connect(this.tabId, this.options);
con.postMessage(data);
resolve(new ExtensionMessageConnect(con));
});

View File

@ -35,6 +35,7 @@ export type ScriptMenuRegisterCallbackValue = {
accessKey: string;
tabId: number;
frameId: number;
documentId: string;
};
export function subscribeScriptMenuRegister(

View File

@ -112,6 +112,12 @@ export class PopupClient extends Client {
}
menuClick(uuid: string, data: ScriptMenuItem) {
return this.do("menuClick", { uuid, id: data.id, tabId: data.tabId, frameId: data.frameId });
return this.do("menuClick", {
uuid,
id: data.id,
tabId: data.tabId,
frameId: data.frameId,
documentId: data.documentId,
});
}
}

View File

@ -190,6 +190,7 @@ export default class GMApi {
accessKey: accessKey,
tabId: sender.getSender().tab!.id!,
frameId: sender.getSender().frameId,
documentId: sender.getSender().documentId,
});
}

View File

@ -24,8 +24,9 @@ export type ScriptMenuItem = {
id: number;
name: string;
accessKey?: string;
tabId: number | "background";
frameId: number;
tabId: number; //-1表示后台脚本
frameId?: number;
documentId?: string;
};
export type ScriptMenu = {
@ -52,6 +53,54 @@ export class PopupService {
private runtime: RuntimeService
) {}
genScriptMenuByTabMap(menu: ScriptMenu[]) {
menu.forEach((script) => {
// 创建脚本菜单
if (script.menus.length) {
chrome.contextMenus.create({
id: `scriptMenu_` + script.uuid,
title: script.name,
contexts: ["all"],
parentId: "scriptMenu",
});
script.menus.forEach((menu) => {
// 创建菜单
console.log("create menu", menu);
chrome.contextMenus.create({
id: `scriptMenu_menu_${script.uuid}_${menu.id}`,
title: menu.name,
contexts: ["all"],
parentId: `scriptMenu_${script.uuid}`,
});
});
}
});
}
// 生成chrome菜单
async genScriptMenu(tabId: number) {
// 移除之前所有的菜单
chrome.contextMenus.removeAll();
const [menu, backgroundMenu] = await Promise.all([this.getScriptMenu(tabId), this.getScriptMenu(-1)]);
console.log(menu, backgroundMenu, tabId);
if (!menu.length && !backgroundMenu.length) {
return;
}
// 创建根菜单
chrome.contextMenus.create({
id: "scriptMenu",
title: "ScriptCat",
contexts: ["all"],
});
if (menu) {
this.genScriptMenuByTabMap(menu);
}
// 后台脚本的菜单
if (backgroundMenu) {
this.genScriptMenuByTabMap(backgroundMenu);
}
}
async registerMenuCommand(message: ScriptMenuRegisterCallbackValue) {
// 给脚本添加菜单
const data = await this.getScriptMenu(message.tabId);
@ -65,6 +114,7 @@ export class PopupService {
accessKey: message.accessKey,
tabId: message.tabId,
frameId: message.frameId,
documentId: message.documentId,
});
} else {
menu.name = message.name;
@ -72,8 +122,10 @@ export class PopupService {
menu.tabId = message.tabId;
}
}
console.log(data);
Cache.getInstance().set("tabScript:" + message.tabId, data);
console.log("set menu", data);
await Cache.getInstance().set("tabScript:" + message.tabId, data);
console.log("update menu");
this.updateScriptMenu();
}
async unregisterMenuCommand({ id, uuid, tabId }: { id: number; uuid: string; tabId: number }) {
@ -83,7 +135,21 @@ export class PopupService {
if (script) {
script.menus = script.menus.filter((item) => item.id !== id);
}
Cache.getInstance().set("tabScript:" + tabId, data);
await Cache.getInstance().set("tabScript:" + tabId, data);
this.updateScriptMenu();
}
updateScriptMenu() {
// 获取当前页面并更新菜单
chrome.tabs.query({ active: true }, (tabs) => {
console.log("query", tabs);
if (!tabs.length) {
return;
}
const tab = tabs[0];
// 生成菜单
tab.id && this.genScriptMenu(tab.id);
});
}
scriptToMenu(script: Script): ScriptMenu {
@ -225,15 +291,34 @@ export class PopupService {
});
}
menuClick({ uuid, id, tabId, frameId }: { uuid: string; id: number; tabId: number; frameId: number }) {
menuClick({
uuid,
id,
tabId,
frameId,
documentId,
}: {
uuid: string;
id: number;
tabId: number;
frameId: number;
documentId: string;
}) {
// 菜单点击事件
console.log("click menu", uuid, id, tabId);
this.runtime.sendMessageToTab(tabId, "menuClick", {
uuid,
id,
console.log("click menu", uuid, id, tabId, frameId, documentId);
this.runtime.sendMessageToTab(
tabId,
frameId,
});
"menuClick",
{
uuid,
id,
tabId,
},
{
frameId,
documentId: documentId,
}
);
return Promise.resolve(true);
}
@ -250,10 +335,47 @@ export class PopupService {
// 清理数据
Cache.getInstance().del("tabScript:" + tabId);
});
// 监听页面切换加载菜单
chrome.tabs.onActivated.addListener((activeInfo) => {
this.genScriptMenu(activeInfo.tabId);
});
// 处理chrome菜单点击
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
const menuIds = (info.menuItemId as string).split("_");
if (menuIds.length === 4) {
const [, , uuid, id] = menuIds;
// 寻找menu信息
const menu = await this.getScriptMenu(tab!.id!);
const script = menu.find((item) => item.uuid === uuid);
if (script) {
const menuItem = script.menus.find((item) => item.id === parseInt(id, 10));
if (menuItem) {
this.menuClick({
uuid: script.uuid,
id: menuItem.id,
tabId: tab!.id!,
frameId: menuItem.frameId || 0,
documentId: menuItem.documentId || "",
});
return;
}
}
}
});
// 监听运行次数
this.mq.subscribe(
"pageLoad",
async ({ tabId, frameId, scripts }: { tabId: number; frameId: number; scripts: ScriptMatchInfo[] }) => {
async ({
tabId,
frameId,
scripts,
}: {
tabId: number;
frameId: number;
document: string;
scripts: ScriptMatchInfo[];
}) => {
this.addScriptRunNumber({ tabId, frameId, scripts });
// 设置角标和脚本
chrome.action.getBadgeText(

View File

@ -122,12 +122,20 @@ export class RuntimeService {
}
// 给指定tab发送消息
sendMessageToTab(tabId: number, action: string, data: any) {
sendMessageToTab(
tabId: number,
action: string,
data: any,
options?: {
documentId?: string;
frameId?: number;
}
) {
if (tabId === -1) {
// 如果是-1, 代表给offscreen发送消息
return sendMessage(this.sender, "offscreen/runtime/" + action, data);
}
return sendMessage(new ExtensionContentMessageSend(tabId), "content/runtime/" + action, data);
return sendMessage(new ExtensionContentMessageSend(tabId, options), "content/runtime/" + action, data);
}
async getPageScriptUuidByUrl(url: string) {

View File

@ -30,6 +30,7 @@
"activeTab",
"webRequest",
"userScripts",
"contextMenus",
"declarativeNetRequest"
],
"host_permissions": [