移除Chrome扩展Manifest权限后,Popup无法获取Content脚本数据
Chrome扩展权限移除后Popup无法获取Content Script数据
我提交Chrome扩展首个版本时,因平台要求处理主机权限,移除了Manifest中以下权限段:
"permissions": [ "scripting", "tabs" ]
移除后,popup中的代码无法获取数据,result返回null;保留权限时可正常工作。
Popup中失效的代码(pop.js)
//Fetching Data from Content Script (async () => { const [tab] = await chrome.tabs.query({active: true, currentWindow: true}); const [{result}] = await chrome.scripting.executeScript({ target: {tabId: tab.id}, func: () => dataPromise, }).catch(() => [{}]); console.log('Result Begin') console.log(result) console.log('Result Ends') })();
Content Script核心逻辑片段(content.js)
const dataPromise = chrome.runtime.sendMessage({from:'send_url',msg:document.location.href}).then( result => { ..... return {'status':resultType1,'missing':missingLinks,'found':foundLinks,'similar':similarLinks} })
完整Manifest文件
{ "name": "My Ex", "description": "My Ext", "version": "1.0.0", "manifest_version": 3, "background": { "service_worker": "background.js" }, "content_scripts": [ { "all_frames": false, "run_at" : "document_end", "matches": [ "<all_urls>" ], "js": [ "content_scripts/jquery.js", "content_scripts/functions.js", "content_scripts/content.js" ] } ], "action": { "default_popup": "index.html", "default_title": "Agile SEO Super Cluster", "default_icon": { "16": "16x16.png", "32": "32x32.png" } }, "web_accessible_resources": [ { "resources": ["bootstrap.min.css", "index.html"], "matches": ["<all_urls>"] } ], "permissions": [ "scripting", "tabs" ] }
完整Content.js代码
console.log('In Content Script') console.clear() const dataPromise = chrome.runtime.sendMessage({from:'send_url',msg:document.location.href}).then( result => { var outLinksClean = [] //without http and www var uniqueDomains = [] var pageLinksBeforeAdditional = [] // Links without count attribute which are before see additional.. var filteredLinks = [] var outLinks = [] var outLinksClusterKeywords = [] var outLinks_keywords = [] var absLink = '' var resultType1 = null var missingLinks = null var foundLinks = null var foundHeadings = [] var similarLinks = null // console.log(result) // HTML returned from remote API if (result.html) { var html = '<div id="asc_div"><div id="rendered">' + result.html + '</div></div>'; var $html = $(html); var y = $html.add($('*', $html)) //Flatten the DOM outLinks_keywords = result.rules['link_destination'] // Converting Keywords to lowercase outLinks_keywords = outLinks_keywords.map(function(a) { a.content_cluster_keyword = a.content_cluster_keyword.toLowerCase(); return a; }); // console.clear() for(var url of outLinks_keywords) { outLinks.push(url['content_cluster_main_page_url']) outLinksClusterKeywords.push(url['content_cluster_keyword']) } for(var url of outLinks){ url = url.replace('http://','').replace('https://','').replace('www.','') outLinksClean.push(url) } if(outLinks.length > 0) { uniqueDomains = getUniqueDomains(outLinks) } var new_dom = isolateAdditionalLinks($html.find('#rendered').children()) var baseDomain = getDomainFromURL(document.location.href) var links = new_dom.find('a').each(function(index) { if (typeof ($(this).attr('count')) == 'undefined') { if (typeof ($(this).attr('href')) != 'undefined') { // similar(outLinksClusterKeywords,$(this).text()) absLink = relativeToAbsolute($(this).attr('href'),baseDomain) // console.log('ABS LINK = '+absLink+' - '+$(this).text()) // pageLinksBeforeAdditional.push(absLink) pageLinksBeforeAdditional.push({'link':absLink,'text':$(this).text().trim()}) } } }) // These are the links that are relevant to the pages. All Operations will be performed against them filteredLinks = filterLinks(uniqueDomains,pageLinksBeforeAdditional) var validationResponse = validateType1(filteredLinks,outLinksClean) resultType1 = validationResponse[0] missingLinks = validationResponse[1] foundLinks = validationResponse[2] similarLinks = validationResponse[3] // console.log('Found Links') // console.log(foundLinks) // console.log('Missing Links') // console.log(missingLinks) // We will only go for optimization if Link Type1 is valid invalid_optimization1_links = [] invalid_optimization2_headings = [] if(resultType1) { // Optimization No1 for Link Type 1 /** * Processing Optimization 1 of Type1. * Type1 Optimization 1 says that the anchor text of the valid found * links must be exact match or 80% similar */ idx = 0 var score = 0 const OPTIMIZATION_COUNT_TYPE_1 = 1 var optimization_1_score = 0 var optimization_2_score = 0 var valid_optimizations_1 = 0 var valid_optimizations_2 = 0 var optimization_1_result = false var optimization_2_result = false var NO_OF_LINKS = foundLinks.length var TOTAL_OPTIMIZATIONS = NO_OF_LINKS * 1 // Since this is for a single opt only for(var foundLink of foundLinks) { idx++ optimization_1_result = validateType1Optimization1(foundLink,outLinks_keywords) if(idx%2==0) { // NOTE: TEMP CHECK optimization_1_result = false invalid_optimization1_links.push(foundLink) } if(optimization_1_result) { valid_optimizations_1++ } } optimization_1_score = (valid_optimizations_1/TOTAL_OPTIMIZATIONS) * 100 // Optimization No2 for Link Type 1 //All Header Tags console.clear() // TEMP DO REMOVE IT AFTER DEBUGGING var headings = new_dom.find(':header').each(function(index) { // optimization_2_result = validateType1Optimization2(outLinksClusterKeywords,$(this).text().toLowerCase()) optimization_2_result = validateType1Optimization2($(this).text().toLowerCase(),outLinks_keywords) // console.log(`optimization_2_result for ${$(this).text().toLowerCase()}`) // Saving Headings not found on page if(!optimization_2_result) { invalid_optimization2_headings.push($(this).text().toLowerCase()) } else { foundHeadings.push($(this).text().toLowerCase()) } if(optimization_2_result) { valid_optimizations_2++ } }) console.clear() // TEMP console.log('outLinksClusterKeywords') console.log(outLinksClusterKeywords) console.log('Found Headings') console.log(foundHeadings) // This is being done to give suggestions for headings not on Page var b1 = new Set(foundHeadings); var notFoundHeadings = [...new Set(outLinksClusterKeywords.filter(x => !b1.has(x)))]; console.log('REMAINING....') console.log(notFoundHeadings) console.log('valid_optimizations_2 = '+valid_optimizations_2) console.log('TOTAL_OPTIMIZATIONS = '+TOTAL_OPTIMIZATIONS) optimization_2_score = (valid_optimizations_2/TOTAL_OPTIMIZATIONS) * 100 console.log('optimization_1_score = '+optimization_1_score) console.log('optimization_2_score = '+optimization_2_score) score = (optimization_1_score+optimization_2_score) / 2 // Because two optimization console.log('Final Score = '+score) } //Sending Message for Visual Status updatation. chrome.runtime.sendMessage({from:'set_icon',msg:resultType1}, res => {}); } return { 'status':resultType1, 'missing':missingLinks, 'found':foundLinks, 'similar':similarLinks, 'total_score':score, 'invalid_optimization1_links':invalid_optimization1_links, 'invalid_optimization2_headings':notFoundHeadings } })
问题原因与解决方案
原因分析
tabs权限依赖:chrome.tabs.query需要tabs权限才能获取当前标签页的ID信息,移除后调用会失败,导致tab变量无有效值。scripting权限依赖:chrome.scripting.executeScript必须拥有scripting权限才能向目标标签页注入脚本并获取返回值,无此权限时调用直接进入catch分支,返回[{}],最终result为null。
解决方案
方案1:改用消息传递替代scripting.executeScript
通过Chrome扩展的消息API实现popup与content script的通信,无需依赖tabs和scripting权限:
- 修改popup代码:
// Popup发送请求,等待content script返回数据 (async () => { try { const result = await chrome.runtime.sendMessage({from: 'popup_request_data'}); console.log('Result Begin') console.log(result) console.log('Result Ends') } catch (err) { console.error('获取数据失败:', err); } })();
- 修改content.js代码:
添加消息监听器,响应popup的数据请求:
// 在content.js末尾添加 chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.from === 'popup_request_data') { // 返回已处理完成的dataPromise结果 dataPromise.then(result => sendResponse(result)); // 异步响应需返回true告知Chrome等待结果 return true; } });
方案2:保留必要权限并声明主机权限
若平台仅要求明确主机权限而非完全移除权限,可调整Manifest配置,保留核心权限并添加主机匹配规则:
"host_permissions": ["<all_urls>"], "permissions": ["scripting"]
注:Manifest V3中,仅查询当前窗口活跃标签时,chrome.tabs.query可无需tabs权限(需测试验证),若仍有问题可保留tabs权限。
内容的提问来源于stack exchange,提问作者Volatil3
相关产品推荐
相关产品推荐

