前言
最近有个需求,是在浏览器插件中获取 window
对象下的某个数据,当时觉得很简单,和 document
一样,直接通过嵌入 content_scripts
直接获取,然后使用 sendMessage
发送数据到插件就行了,结果发现不是这样滴…
这玩意还是个挺麻烦的点,下面给出三种解决方案
在这里不推荐使用
runtime.executeScript
进行注入,很可能会报错:
Refused to execute inline script because it violates the following Content Security Policy directive: “script-src ‘self’ ‘wasm-unsafe-eval’ ‘inline-speculation-rules’ http://localhost:* http://127.0.0.1:*”. Either the ‘unsafe-inline’ keyword, a hash (‘sha256-P5exJBBLYN1KVh+CK9MkXvRal4ZQQu9VaKPvx4JuVLE=’), or a nonce (‘nonce-…’) is required to enable inline execution.
一、两个 JS 文件,通过 postMessage 传递消息
1. 方案思路
- 新建两个
js
文件,index.js
和lucky.js
- 在
content_scripts
中嵌入index.js
文件 - 在
index.js
中通过script
标签,嵌入lucky.js
- 在
index.js
中通过window.addEventListener('message')
监听消息 - 在
lucky.js
中通过window.postMessage
进行消息传递 - 在
manifest.json
文件中添加web_accessible_resources
1.1. 方案流程
流程图如下:
2. 获取内容
获取
window
下的MyBlog
字段
window.MyBlog = {
juejin: 'https://juejin.cn/user/2409752520033768/posts',
csdn: 'https://guoqiankun.blog.csdn.net/',
'chrome-blog': {
netlify: 'https://gqk-extension.netlify.app/',
github: 'https://18055975947.github.io/extension/'
}
}
3. 实现代码
3.1. index.js
const init = () => {
const script = document.createElement('script')
script.src = chrome.runtime.getURL('lucky.js')
document.head.appendChild(script)
// 监听从页面上下文发回的消息
window.addEventListener('message', (event) => {
console.log('event', event)
if (event.source !== window) return
if (event.data.type === 'get-window-data') {
console.log('window data:', event.data.data)
}
})
}
// 判断 window.top 和 self 是否相等,如果不相等,则不注入
if (window.top == window.self) {
init()
}
3.2. lucky.js
window.postMessage({
type: 'get-window-data',
data: window.MyBlog,
file: 'lucky'
})
3.3. manifest.json
{
"manifest_version": 3,
"name": "Get Winddow Object Field",
"version": "1.0",
"description": "Gets the field under window",
"content_scripts": [
{
"js": [
"index.js"
],
"matches": ["http://localhost:*/*"],
"all_frames": true,
"run_at": "document_end"
}
],
"background": {
"service_worker": "service-worker.js"
},
"host_permissions": [
"http://localhost:*/*"
],
"permissions": [
],
"web_accessible_resources": [
{
"resources": ["lucky.js"],
"matches": ["http://localhost:*/*"],
"use_dynamic_url": true
}
]
}
3.4. 项目文件结构
.
├── index.html
├── index.js
├── lucky.js
├── manifest.json
└── service-worker.js
3.5. 方案效果
在控制台中选择当前插件,即可查看获取的 window
下的 MyBlog
对象
4. 动态获取参数
如果想通过点击等操作获取数据,则方法如下
4.1. 实现流程
如果想通过页面按钮点击再获取数据,也是通过 postMessage
发送消息,以及通过 addEventListener
接收消息再次发送到插件
流程图如下:
4.2. 实现代码
4.2.1. index.js
/**
* 发送消息
* @param {string} type 类型
* @param {string} field 获取的字段名称
*/
const postMessage = (type, field) => {
window.postMessage({
type,
field,
file: 'index'
})
}
/**
* 初始化
*/
const init = () => {
const script = document.createElement('script')
script.src = chrome.runtime.getURL('lucky.js')
document.head.appendChild(script)
// 监听从页面上下文发回的消息
window.addEventListener('message', (event) => {
console.log('event', event)
if (event.source !== window) return
if (event.data.type === 'get-window-data') {
console.log('window data:', event.data.data)
}
})
// 加入定时器是因为 lucky.js 加载需要时间,如果不需要初始化就获取数据,可以把这行删除
// 如果想初始化就获取数据,又不想加 setTimeout,就把下面 lucky.js 注释的代码放开就行
setTimeout(() => postMessage('get-window-field', 'MyBlog'), 100)
// 或者在 script onload 的时候进行消息发送
// script.onload = () => {
// postMessage('get-window-field', 'MyBlog')
// }
// 插入 button 按钮
const button = document.createElement('button')
button.innerText = '获取数据'
button.id = 'chrome-ext-but'
document.body.appendChild(button)
button.onclick = () => {
postMessage('get-window-field', 'MyBlog')
}
}
// 判断 window.top 和 self 是否相等,如果不相等,则不注入
if (window.top == window.self) {
init()
}
4.2.2. 点击按钮获取数据
4.2.3. lucky.js
// 如果想初始化就传递消息,也不想加 setTimeout,则放开下面的代码
// window.postMessage({
// type: 'get-window-data',
// data: window.MyBlog,
// file: 'lucky'
// })
/**
* 监听 message 消息
*/
window.addEventListener('message', (event) => {
if (event.data?.type === 'get-window-field') {
window.postMessage({
type: 'get-window-data',
data: window[event.data.field],
file: 'lucky'
})
}
})