具体如何编写Chrome扩展,可以查看Google提供的API文档。本次我打算编写一个简易版本,右键提供一个上下文菜单,复制文本。一些文本屏蔽的内容较少,也有方法查看。如下图所示。
准备图标
新建文件夹CopyText,这将是我们的插件目录。
前往iconfont选择一个合适的图标,尺寸准备16,32,48,64,128
想要获得代码提示,需要在WebStorm中导入库。
点击download,找到chrome
现在,你就可以获得代码提示功能。
编写清单文件(manifest.json
)
{
"name": "Copy Text",
"version": "0.1",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"icons": {
"16": "icons/copy16.png",
"32": "icons/copy32.png",
"48": "icons/copy48.png",
"64": "icons/copy64.png",
"128": "icons/copy128.png"
}
}
manifest_version
非常重要,只能写成数字3,manifest_version2在未来会被废弃。
打开开发者模式并且加载文件夹
编写后台脚本
MV3的后台脚本使用了service worker
我们将要使用到contextMenus,因此需要在manifest.json中声明权限。
查看API文档使用即可。
我尝试了解除阻止右键菜单事件,发现不好处理。因为Event.addEventListener触发事件为匿名函数就不好处理。
为了,我们采取捕获网络请求的方式。Chrome提供了webRequest
用于捕获请求,同时完成剪切操作需要scripting
和tabs
为此声明权限:
在Chrome中捕获请求
这就是我们要捕获的请求;
async function getPageUrl() {
let pagesRequest = []
chrome.webRequest.onHeadersReceived.addListener(details => {
// console.log(details.url)
if (details.url.indexOf('0.json') !== -1) {
pagesRequest.push(details.url)
}
}, {urls: ["https://*.bdimg.com/*"]})
return pagesRequest
}
拿到url后排序
urls.sort((u1, u2) => {
let q1 = getQuery(u1);
let q2 = getQuery(u2);
let s1 = q1['x-bce-range'].split('-')[0];
let s2 = q2['x-bce-range'].split('-')[0];
return s2 - s1
})
function getQuery(u) {
const url = decodeURI(u); // 获取url中"?"符后的字串(包括问号)
let query = {};
if (url.indexOf("?") !== -1) {
const str = url.substr(1);
const pairs = str.split("&");
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i].split("=");
query[pair[0]] = pair[1];
}
}
return query; // 返回对象
}
将他们拼接成文本
async function collectParagraph(...urls) {
let article = ""
for (let url of urls) {
console.log('当前url:', url);
let response = await fetch(url, {
"headers": {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9",
"sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "script",
"sec-fetch-mode": "no-cors",
"sec-fetch-site": "cross-site"
},
"referrer": "https://wenku.baidu.com/",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "omit"
});
let result = await response.text()
let content = JSON.parse(result.substring(8, result.length - 1));
for (let para of content.body) {
// console.log(para.c)
article += para.c;
if (para.ps != null)
for (let i = 0; i < para.ps["_enter"]; i++) {
article += '\n';
}
}
}
return article;
}
复制到剪切板
async function copyText(text) {
let currentTab = await getCurrentTab();
await chrome.scripting.executeScript({
target: {tabId: currentTab.id},
func: (text) => {
navigator.clipboard.writeText(text).then(function () {
console.log("success")
}, function () {
console.log("failed")
});
},
args: [text]
})
}
主函数执行整个过程
(async () => {
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
contexts: ['selection', 'page'],
title: "复制文本",
id: "copyMenu",
})
})
let urls = await getPageUrl();
urls.sort((u1, u2) => {
let q1 = getQuery(u1);
let q2 = getQuery(u2);
let s1 = q1['x-bce-range'].split('-')[0];
let s2 = q2['x-bce-range'].split('-')[0];
return s2 - s1
})
async function handleContextMenus(info) {
let article = ""
if (info.menuItemId === 'copyMenu' && info.selectionText !== undefined) {
await copyText(info.selectionText)
} else if (info.menuItemId === 'copyMenu' && info.pageUrl.indexOf('https://wenku.baidu.com') !== -1) {
article += await collectParagraph(...urls)
await copyText(article)
}
}
chrome.contextMenus.onClicked.addListener((async info => {
await handleContextMenus(info);
}))
})();
最后清单文件是:
{
"name": "Copy Text",
"version": "0.1",
"manifest_version": 3,
"author": "叶秋明",
"description": "复制文本和获取百度文库的内容",
"background": {
"service_worker": "background.js"
},
"icons": {
"16": "icons/copy16.png",
"32": "icons/copy32.png",
"48": "icons/copy48.png",
"64": "icons/copy64.png",
"128": "icons/copy128.png"
},
"permissions": [
"contextMenus",
"tabs",
"scripting",
"webRequest"
],
"host_permissions": [
"https://wenku.baidu.com/*",
"https://*.bdimg.com/*",
"<all_urls>"
]
}