You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展右键菜单触发函数重复执行问题排查

Zendesk Chrome扩展右键菜单重复执行问题

问题描述

我正在开发一款面向Zendesk的Chrome扩展,核心功能之一是收集指定工单中两种格式的字符串,以摘要或「时间线」格式输出。目前功能可正常运行,但存在一个棘手bug:当选择扩展自定义右键菜单选项时,函数执行次数等于用户选择菜单前的右键点击次数。

当前实现逻辑是:运行在页面上下文的content script向background script发送消息,初始化右键菜单的onClicked监听器后,再由background向content script发送更新触发函数。曾尝试反向实现,但由于content script需要在页面上下文访问CKEditor实例,无法完成。推测每次右键点击都会将消息加入队列,选择菜单项时队列被一次性清空,导致重复执行。

相关代码

script.js(Content Script)

function generateTimeline(editingArea)
{
   chrome.runtime.sendMessage("nmkeddobbgmklaojjmkimpaffccencgn", {operation : "UPD" }, function(response) {
      if(response === "UPDATED")
      {
         const editorInstance = editingArea.ckeditorInstance;
         const eventContainer = getEventContainer(editingArea);
         let dateTimeRecords = getInternalCommentTextNodes(eventContainer);
         dateTimeRecords.sort(dateComparator);
         duplicateChecker(dateTimeRecords);
         
         editorInstance.model.change(writer => {
            for(record of dateTimeRecords)
            {
               const paragraph = writer.createElement('paragraph');
               writer.append(paragraph, editorInstance.model.document.getRoot());
               const textNode = writer.createText(record.tag + ' ' + record.comment);
               writer.append(textNode, paragraph);
            }
         });
      }
   });
}

document.addEventListener('mouseup', function(e) {
   
   if(this.activeElement.getAttribute("role") === "textbox" && this.activeElement.getAttribute("aria-label") === "Rich Text Editor. Editing area: main")
   {
      const editingArea = singletonEventListener(this.activeElement);
      // 左键点击
      if(e.button === 0) 
      {
         createKeyPressListener(editingArea);
      }
      // 右键点击
      if(e.button === 2)
      {
         generateTimeline(editingArea);
      }
   }
});

background.js

const timeLineGenerator = {
    "id": "timeLineGenerator",
    "title": "Generate Timeline",
    "contexts":["editable"]
};


chrome.runtime.onInstalled.addListener(() => {
    chrome.contextMenus.create(timeLineGenerator);
});

function manageMenuListener(request, sender, sendResponse)
{
    if(request.operation === "UPD")
    {
        if(chrome.contextMenus.onClicked.hasListener) 
        {
            chrome.contextMenus.onClicked.removeListener();
            console.log("listener removed");
        }
        
        chrome.contextMenus.onClicked.addListener(() => {
            return sendResponse("UPDATED");
        });
    } 
}
chrome.runtime.onMessageExternal.addListener(manageMenuListener);

manifest.json

{
    "manifest_version": 3,
    "name": "Zendesk Date Time Tool",
    "description": "Inserts a date time string when a user defined key is pressed",
    "permissions":[
        "scripting",
        "contextMenus",
        "activeTab",
        "tabs"
    ],
    "host_permissions":["<all_urls>"],
    "version": "1.0",
    "icons":{
        "16":"/icons/icon16.png",
        "48":"/icons/icon48.png",
        "128":"/icons/icon128.png"
    },
    "action": {
        "default_popup": "/src/index.html",
        "default_icon": "/icons/icon128.png"
    },
    "background":{
        "service_worker":"/src/background.js"
    },
    "content_scripts":[{
        "world": "MAIN",
        "matches":["https://*.zendesk.com/*"],
        "js":["/src/script.js"],
        "run_at":"document_end"
    }],
    "externally_connectable": {
        "matches": ["https://*.zendesk.com/*"]
      } 
}

编辑记录

  1. 将return sendResponse("UPDATED");移至独立的triggerTimeline函数后,触发错误:Error in event handler: TypeError: sendResponse is not a function at triggerTimeline,修改后的background.js代码:
const timeLineGenerator = {
    "id": "timeLineGenerator",
    "title": "Generate Timeline",
    "contexts":["editable"]
};


chrome.runtime.onInstalled.addListener(() => {
    chrome.contextMenus.create(timeLineGenerator);
});
function triggerTimeline(request, sender, sendResponse)
{
    return sendResponse("UPDATED");
}
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse){
    if(request.operation === "UPD")
    {
       
        if(chrome.contextMenus.onClicked.hasListener(triggerTimeline)) 
        {
            chrome.contextMenus.onClicked.removeListener(triggerTimeline);
            console.log("listener removed");
        }
        chrome.contextMenus.onClicked.addListener(triggerTimeline);
    } 
});
  1. 使用全局函数变量存储triggerTimeline后的background.js代码:
const timeLineGenerator = {
    "id": "timeLineGenerator",
    "title": "Generate Timeline",
    "contexts":["editable"]
};


chrome.runtime.onInstalled.addListener(() => {
    chrome.contextMenus.create(timeLineGenerator);
});

let triggerTimeline;

chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse){
    
    triggerTimeline = function()
    {
        return sendResponse("UPDATED");
    }
    if(request.operation === "UPD")
    {
       
        if(chrome.contextMenus.onClicked.hasListener(triggerTimeline)) 
        {
            chrome.contextMenus.onClicked.removeListener(triggerTimeline);
            console.log("listener removed");
        }
        chrome.contextMenus.onClicked.addListener(triggerTimeline);
    } 
});
  1. 修改后解决了重复执行问题,但函数无法稳定触发,代码如下:
const timeLineGenerator = {
    "id": "timeLineGenerator",
    "title": "Generate Timeline",
    "contexts":["editable"]
};


chrome.runtime.onInstalled.addListener(() => {
    chrome.contextMenus.create(timeLineGenerator);
});

let triggerTimeline;

chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse){
    if(!triggerTimeline)
    {
        triggerTimeline = function()
        {
            return sendResponse("UPDATED");
        }
    }
    if(request.operation === "UPD")
    {
        if(chrome.contextMenus.onClicked.hasListener(triggerTimeline)) 
        {
            chrome.contextMenus.onClicked.removeListener(triggerTimeline);
            console.log("listener removed");
        }
        chrome.contextMenus.onClicked.addListener(triggerTimeline);
    } 
});

内容的提问来源于stack exchange,提问作者Gnome-Improvement713

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 02:07:37