Chrome 扩展在页面执行 JS

记录下浏览器扩展开发时要在页面环境中执行 JS 的解决方案。

基于安全考虑,Chrome 扩展运行在一个独立的沙箱里,不能直接接触页面里的变量和实例。但有些时候我们必须在当前页面的环境里执行 JS,比如近期开发 Navlang 时遇到一个需求:修改浏览器环境里语言信息,实现语言切换。

修改 navigator.language

语言切换可以通过修改 navigator.languagenavigator.languages 实现。这里大家可能会遇到另一个问题:当我们执行 navigator.language = 'en' 后,再次访问 navigator.language,返回的仍然是赋值前的结果。这是因为 navigator 里的值,包括常用的 userAgent 在内,其实都是通过 getter 函数返回的,直接赋值无效。所以这里我们需要用 ES5 里新增的 Object.defineProperty 来操作:

Object.defineProperty(navigator, "language", {
  get: () => $lang,
});
Object.defineProperty(navigator, "languages", {
  get: () => ([$lang]),
});

这里还用到 ES6 中的箭头函数,尤其返回 languages 的时候,因为是数组,还用到一些 ES6 中的解构语法,如果不太清楚的话可以找一些资料来看看,或者留言。

在页面里执行 JS

接下来,插件运行环境里也有 window 实例,提供完整的 BOM 实现,包括 navigator 等对象,所以我一开始没反应过来,直接就在插件里跑,执行也不报错,但是效果出不来。

来回试了几次,终于想起来,它们环境不一样,我在插件里改变了的 navigator.language,和网页里的不是一个。所以虽然程序执行都正常,但是没有实际生效。

经过一番 Google,终于找到解决方案。因为执行环境不同,所以直接访问肯定不行,目前可行的方案则是操作 DOM,注入一个 <script>,把 JS 放进去,这样就可以在页面的环境里执行 JS 了。

function injectScript(content) {
  const script = document.createElement('script');
  script.textContent = `(${asyncFunction})()`;
  document.documentElement.appendChild(script);
  script.remove();
}

作者: meathill

爱编程,爱旅游,爱吐槽。 今年的目标是完成并运营至少一个 Side Project。 《Electron + Vue 实战开发》龟速创作中……

欢迎吐槽,请勿装死

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据