升级浏览器扩展到 MV3 时,因为 MV3 不再允许使用 background 页面,必须使用 service worker 类型的 background script。这样一来,就没有 window
对象,会遇到很多问题。
今天分享下如何在 service worker 类型的 background script 里完成扫描二维码并传出结果。比如我帮朋友升级 二维码生成器 (Quick QR) 这个扩展,它有个功能是通过右键菜单扫描页面中的二维码,并返回扫描结果。这个过程只能在 background script 里完成,于是升级时就给我带来了不小的麻烦。
首先,右键菜单的响应事件只能得到图片 URL。其次,扫描功能需要用到 jsqr,它接受 imageData 作为参数,可以返回扫描结果。所以我们就需要把 URL 转换成 imageData,过程如下:
// service worker 里可以使用 `fetch()` 请求服务器
const response = await fetch(src);
const blob = await response.blob();
// 使用 `createImageBitmap` 可以将支持的图片格式转换成位图
const bitmap = await createImageBitmap(blob);
const {width, height} = bitmap;
// 在 service worker 里,需要使用 OffscreenCanvas
const canvas = new OffscreenCanvas(width, height);
// 注意,这里只能是 `2d`,`bitmaprenderer` 无法使用 `getImageData` 方法
const context = canvas.getContext('2d');
context.drawImage(bitmap, 0, 0);
const imageData = context.getImageData(0, 0, width, height);
// 接下来识别就好
const data = jsQR(imageData.data, width, height);
const [tab] = await getActiveTab();
// 因为没有 `window`,`alert()`、`prompt()` 都不能用,只能通过在目标页面执行脚本的方式输出结果。注意,`tab.executeScript` 也没有了,必须用下面这个新 API,并且要提前申请权限
await chrome.scripting.executeScript({
args: [i18n(SCAN_RESULT), data.data],
target: {
tabId: tab.id,
},
func: (message, result) => {
result = prompt(message, result);
if (result) {
navigator.clipboard.writeText(result)
.catch(() => void 0);
}
},
});
大概过程如上面代码所示,需要注意的东西我都放在注释里了。
关于 MV3 的其它升级经验,我会更新在 Chrome Extension Manifest V3 升级笔记,欢迎常来看看。
其实我一开始用的另一套方案,但是 Chrome 96 之后那套方案就失败了,不太确定问题出在哪里,估计是兼容性问题。然后前几天把这个项目翻出来尝试修复,竟然成功了,于是写篇文章记录下。
关于 imageBitmap 和 imageData 的区别,前者是准备好绘制的位图信息,交给 GPU 就能画出来;后者是基于特定图片格式的二进制信息。感兴趣的同学可以看 这个答案。
欢迎留言讨论。如果关于浏览器扩展 MV3 有任何问题,也欢迎随时联系我。
欢迎吐槽,共同进步