谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

1 前言

大家好,我是心锁,一枚23届准毕业生。

近期在做一个Chrome浏览器截图插件,功能是从浏览器截图并发送图片到企微,便于在远程办公环境下快速从浏览器发送图片进行showCase(目前未真正使用上,原因是截图时html2canvas有错位)

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第1张图片

在开发浏览器插件时,有时候会遇到并不需要全屏截图的情况。比如,截图的时候忽略侧边栏只截图右侧的内容区。

所以在这个简单的需求下,我尝试通过允许用户自定义hook来实现

2 解决方案

我们要完成hook,本质上就是两种方法。

一种是动态代码注入(文件注入),一种是eval执行字符串

2.1 已知限制

开始之前,我们首先先获取一些(踩坑后明白的)已知限制

  • Chrome v3版本全面禁止eval, new Function,不管在background,inject还是popup中都无法通过任何手段开启

  • Chrome v3版本全面禁止通过script标签加载外部源文件

  • Chrome目前上的的插件仅支持v3,v2已经禁上架

那么在此基础上,我们能评估出哪些方案呢?

2.2 方案评估

2.3.1 unsafe-wasm-eval【❌】

经过chrome文档的查阅,发现在v3版本中允许对wasm文件做动态导入的操作

87923D69A780C4012B16BDA5F4718552

但是,WebAssembly的难度一下子击中我的心巴

不必多说,且往下看

2.3.2 动态注入chrome.tabs.executeScript【❌】

在查阅API文档时发现,chrome支持程序注入,可以胜任动态注入脚本

chrome.action.onClicked.addListener((tab) => {
  chrome.scripting.executeScript({
    target: { tabId: tab.id },
    files: ['content-script.js']
  });
});

那么我们现在需要考虑的就是能否生成文件并注入。

这就需要两个条件:

  • 可以生成文件,即File对象存在
  • 可以将文件转换成地址用于注入

对于前者是没有问题的

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第2张图片

但是很遗憾,对于将文件转换成地址用于注入常用的APIURL.createObjectURL是不支持的

image-20220905162507492

2.3.3 在inject里做URL注入呢?【❌】

结合2.3.2,我们可以获得文件内容~

既然background不支持获得URL,那么我们在inject中跑行不行呢?

确实,我们可以拿到URL

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第3张图片

传递URL到background之后再插入到content中

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第4张图片

很遗憾还是不行,会直接找不到文件

image-20220905195514963

而另一个方案是在content中注入script,这是实践内容,答案是不行。

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第5张图片

2.3.4 JS解释器【】

否决了三个方案,最后回到了通过eval的方式解决问题的方法。

上文说过,Chrome全面禁止了eval,但是这并非绝对的。

0E9417AA32C9237EE375B432C56D18F4

我们知道,JS作为一款解释性语言,是无法从根源上切断JS的运行的。

Chrome固然禁止了我们使用系统携带的eval解释器,但是并不妨碍我们引入自己的解释器。

48C0DB306922DBD18A546DAD5C9A5920

一番搜索,找到了曾经超diao的eval5

  • https://github.com/bplok20010/eval5

根据eval5的README,我们完全可以在浏览器沙盒环境中使用eval5

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第6张图片

所以,最终方案选择eval5

3 让eval5支持ES6+

上边的截图也看到了,eval5目前只支持ES5,同时从作者的回复来看,并没有计划支持ES6

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第7张图片

但是如果只是这样多没意思,不会有人还在使用ES5开发吧~

383C8600B9482E7EF711D15F6D955F07

所以我们尝试让eval5支持ES6+

3.1 solution

244392FC225E2177F8435874B3A49BE3

ES5和ES6有哪些区别?

其实我们平时使用的箭头函数、const、let全都是ES6+的内容,习惯了现代JavaScript再去支持ES5编程会感觉非常别扭。

而这也造就了,如果我们要支持ES6,去动手修改eval5的解释器工作量会很大。

27C9BA14FEC45B6C24BF60C8F18C84B6

所以转变思路,我们把ES6+代码转换成ES5

3.2 用babel将ES6+字符串转ES5字符串

那自然就引出了老朋友babel,我们来到babel官网

8E0B48BD4AA1E478A961D2C5EC0ECDDB

我们直接揭露谜底,我们本次需要用到的是@babel/standalone集成包

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第8张图片

所以现在,我们可以通过简单的两行代码将ES6+的代码字符串转为ES5字符串

var input = 'const getMessage = () => "Hello World";';
var output = Babel.transform(input, { presets: ["env"] }).code;

在这个基础上,我们尝试一下将Babel与eval5搭配使用。

const { Interpreter } = eval5;

Interpreter.global = window;
const interpreter = new Interpreter();

const baseCode="const getMessage=()=>'Hello World';console.log(getMessage());"
const newCode=Babel.transform(baseCode, { presets: ["env"] }).code
console.log('newCode',newCode)
interpreter.evaluate(newCode);

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第9张图片

很显然,一切OK~

1020055674B596C2B83948ADE0679D33

现在我们可以愉快地在Chrome v3中使用ES6甚至TS语法了

4 总结

0E9417AA32C9237EE375B432C56D18F4

文章结尾,是我把相关的内容抽离成了一个只有18行代码的github仓库,对于懒人朋友们,只需要将打包产物umd.min.js在content_scripts中配置好进行注入,即可在Chrome v3插件中使用eval。

谷歌来了也不好使!谁说Chrome插件v3中不能使用eval?_第10张图片

除此之外,我们对于使用Babel将ES6+转换成ES5+代码的方案还是要给出一些已知的问题:

  • eval5不支持ProxyReflect这些无法被babel兼容的ES6代码,所以我们无法使用这两个特性
  • 众说周知,babel转换是一个废时的操作,大量应用可能引起性能瓶颈

你可能感兴趣的:(前端,chrome,前端,javascript,chrome,devtools)