当前位置: 首页 > article >正文

Chrome 浏览器插件获取网页 iframe 中的 window 对象

Chrome 浏览器插件获取网页 iframe 中的 window 对象

前言

之前写了篇《Chrome 浏览器插件获取网页 window 对象》文章,是获取当前页面的 window 对象,但是有些页面是嵌入 iframe 的,特别是系统项目主域一样,那就也需要获取 iframe 内部的 window 对象数据,而且还不能重复加载 content html 页面。这个时候就需要对 content_script 的 js 文件进行特殊处理了。

一、需求整理

1. 没有 iframe 内嵌

可以参考 《Chrome 浏览器插件获取网页 window 对象》文章

2. 嵌套一个 iframe

2.1. 页面如下

[外链图片转存中…(img-EUQWEaju-1734921003172)]

2.2. Parent 页面对象数据
<script>
window.coverageData = {
  name: 'parent',
  data: {
    a: 'parent'
  }
}
</script>
2.3. Child 页面对象数据
<script>
window.coverageData = {
  name: 'child1',
  data: {
    a: 'child'
  }
}
</script>

3. 嵌套多个 Iframe

3.1. 页面如下

[外链图片转存中…(img-DEdTomCY-1734921003172)]

3.2. Child2 页面对象数据
<script>
  window.coverageData = {
    name: 'child2',
    data: {
      a: 'child2'
    }
  }
</script>

二、需求实现

我们就用 《Chrome 浏览器插件获取网页 window 对象》文章中的第一种方案,使用两个 JS,通过 postMessage 进行消息通讯获取 window 对象数据。

1. 新建项目&文件

.
├── assets
│   └── icon.png
├── index.js
├── lucky.js
├── manifest.json
└── service-worker.js
  • index.js:通过 content_scripts 引入
  • lucky.js:通过 index.js 文件,插入当前页面的 head 标签中

2. manifest.json 文件

{
  "manifest_version": 3,
  "name": "Get Window Data",
  "version": "0.0.1",
  "description": "get window data",
  "action": {
    "default_title": "Get Window Data",
    "default_icon": "assets/icon.png"
  },
  "background": {
    "service_worker": "service-worker.js",
    "type": "module"
  },
  "content_scripts": [
    {
      "js": [
        "index.js"
      ],
      "matches": [
        "http://127.0.0.1:*/*",
        "http://localhost:*/*"
      ],
      "all_frames": true,
      "run_at": "document_end"
    }
  ],
  "host_permissions": [
    "http://127.0.0.1:*/*",
    "http://localhost:*/*"
  ],
  "permissions": [
		"tabs",
    "scripting",
    "activeTab"
  ],
  "web_accessible_resources": [
    {
      "resources": ["lucky.js"],
      "matches": ["http://127.0.0.1:*/*", "http://localhost:*/*"],
      "use_dynamic_url": false
    }
  ]
}
  • content_scripts:
    • 一定要设置 all_frames 为 true,这样才可以透传 iframe
    • matches 匹配的是本地域名,根据项目需要自行修改
  • host_permissions:匹配的是本地域名,根据项目需要自行修改
  • permissions:需要用到的权限,根据项目需要自行修改
  • web_accessible_resources:所有需要在插件代码里面用到的文件,都需要加在 resources 中,不加这个 lucky.js 不能嵌入页面。

3. lucky.js 文件

/**
 * 发送 coverage data 数据
 * @param {boolean} init 是否是初始化数据
 */
const sendCoverageData = (init = false) => {
  window.postMessage({
    type: 'coverage-data',
    data: window.coverageData,
    location: {
      href: window.location.href,
      hostname: window.location.hostname,
      host: window.location.host,
      pathname: window.location.pathname,
      protocol: window.location.protocol,
      port: window.location.port,
      search: window.location.search,
      hash: window.location.hash,
      origin: window.location.origin,
      domain: document.domain,
      title: document.title
    },
    init
  })
}
console.log('luckyjs, window.self === window.top', window.self === window.top)
// JS coverage data
sendCoverageData(true)

/**
 * 监听 message 并进行处理
 */
window.addEventListener('message', (event) => {
  if (event.data?.type === 'get-coverage') {
    sendCoverageData()
  }
})
  • sendCoverageData:通过 postMessage 发送消息
    • init 是值是否第一次发送消息
    • 包含的数据为 window 下的 coverageData、location 对象,type 类型,init 字段
    • 不可以直接把 location 传过来,会报错的
  • addEventListener 进行消息监听,再次发送数据
3.1. 直传 location 报错

Uncaught DataCloneError: Failed to execute ‘postMessage’ on ‘Window’: Location object could not be cloned.

3.1.1. 页面报错消息

[外链图片转存中…(img-0UKCS9xx-1734921003172)]

3.1.2. 插件报错消息

[外链图片转存中…(img-UgOegQTJ-1734921003172)]

3.2. 为什么要传 location 数据?

因为可能存在多个 iframe,可以通过 location 进行区分

4. index.js 文件

/**
 * 添加 script 标签
 * @param {string} url 路径
 * @param {string} id script ID
 */
const addScript = (url, id) => {
  const script = document.createElement('script')
  id && (script.id = id)
  script.src = chrome.runtime.getURL(url)
  document.head.appendChild(script)
}

/**
 * 添加 JS 和事件监听
 */
const addJSAndEventListener = async () => {
  // 监听从页面上下文发回的消息
  window.addEventListener('message', (event) => {
    console.log('event.data', event.data)
    if (event.data?.type === 'coverage-data') {

    }
  })
  addScript('lucky.js', 'coverage-script')
}

addJSAndEventListener()

  • addScript:添加 JS 文件,把 lucky.js 添加到 head 标签中
  • addEventListener:消息监听

5. 安装插件

[外链图片转存中…(img-BKvrYSvE-1734921003172)]

6. window 数据

6.1. 当前页面 console 日志

[外链图片转存中…(img-u1h1mu7y-1734921003172)]

6.2. 当前页面插件 console 日志
6.2.1. 点击下面选项可以插件插件 index.js 的 console 日志

[外链图片转存中…(img-xzqEK568-1734921003172)]

6.2.2. console 日志

[外链图片转存中…(img-IU7N0A26-1734921003172)]

6.3. iframe 页面 console 日志

[外链图片转存中…(img-RufeHsSV-1734921003173)]

6.4. iframe 页面插件 console 日志
6.4.1. 选择当前 iframe 下的插件

[外链图片转存中…(img-7Nyz7P4e-1734921003173)]

6.4.2. console 日志

[外链图片转存中…(img-z4TNdIqI-1734921003173)]

7. 把数据传给 service-worker

7.1. service-worker.js 文件
/**
 * service worker 监听 接收消息
 */
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  console.log('message', message)
  switch (message.action) {
    // 从 content 发送消息到 SW,发送数据
    case 'fromContent2SW-sendCoverage': {
			// todo
      break
    }
    default: {
      break
    }
  }
  sendResponse()
  return false
})

7.2. service-worker 背景日志

[外链图片转存中…(img-DxqqjTnj-1734921003173)]

8. 嵌套多个 iframe

8.1. 查看页面

[外链图片转存中…(img-m5ot8wmH-1734921003173)]

8.2. service-worker 日志

[外链图片转存中…(img-PcuxPrOs-1734921003173)]

三、收集 coverage 数据并展示在 popup 中

[外链图片转存中…(img-qyZTcu1f-1734921003173)]

1. 增加 popup.html 和 popup.js 文件

.
├── assets
│   └── icon.png
├── index.js
├── lucky.js
├── manifest.json
├── popup.html
├── popup.js
└── service-worker.js
1.1. popup.html 文件内容
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Popup</title>
  <style>
    *{
      padding: 0;
      margin: 0;
    }
    body{
      width: 400px;
      max-height: 300px;
      min-height: 200px;
    }
    header{
      text-align: center;
      margin-bottom: 10px;
    }
    main{
      padding: 0 20px;
      #get_window_but{
        margin-bottom: 10px;
      }
      li{
        margin-bottom: 10px;
        word-break: break-all;
        list-style: none;
      }
    }
  </style>
</head>
<body>
  <header>
    <h1>Popup</h1>
  </header>
  <main>
    <button id="get_window_but">获取 window 数据</button>
    <ul id="window_coverage"></ul>
  </main>
</body>
<script src="popup.js"></script>
</html>
1.2. popup.js 文件内容
/**
 * 事件监听
 */
chrome.runtime.onMessage.addListener((e, _, sendResponse) => {
  switch (e.action) {
    case 'fromSW2Popup-sendCoverage': {
      const coverageUl = document.getElementById('window_coverage')
      const li = document.createElement('li')
      li.innerHTML = `
        <div>url: ${e.message.location.href}</div>
        <div>coverage: ${e.message.data ? JSON.stringify(e.message.data) : ''}</div>
      `
      coverageUl.appendChild(li)
      break
    }
    default:
      break
  }
  sendResponse()
  return false
})

const get_window_but = document.getElementById('get_window_but')

/**
 * but 按钮事件
 */
get_window_but.onclick = async () => {
  const tabId = await getCurrentTabId()
  // 从 popup 发送消息到  SW
  tabId && chrome.runtime.sendMessage({
    action: 'fromPopup2SW-getWindowData',
    message: {
      tabId
    }
  })
}

/**
 * 获取当前活动页面的 id
 * @returns {string} tabId
 */
const getCurrentTabId = async () => {
  const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
  return tabs && tabs.length > 0 ? tabs[0].id : ''
}
  • addListener:事件监听,监听从 SW 发送到 popup 的消息,把消息生成 li 在 append 到 ul 中
  • onclick:按钮点击事件,发送消息到 SW
  • getCurrentTabId:获取当前活动的页面的 tabId

2. manifest.json 文件

"action": {
  "default_title": "Get Window Data",
  "default_icon": "assets/icon.png",
  "default_popup": "popup.html"
},
  • 增加 default_popup 字段

3. index.js 文件

/**
 * 添加 script 标签
 * @param {string} url 路径
 * @param {string} id script ID
 */
const addScript = (url, id) => {
  const script = document.createElement('script')
  id && (script.id = id)
  script.src = chrome.runtime.getURL(url)
  document.head.appendChild(script)
}

/**
 * 添加 JS 和事件监听
 */
const addJSAndEventListener = async () => {
  // 监听从页面上下文发回的消息
  window.addEventListener('message', (event) => {
    if (event.data?.type === 'coverage-data') {
      // 从 content 脚本获取到 coverage 数据后,发送给 SW
      chrome.runtime.sendMessage({
        action: 'fromContent2SW-sendCoverage',
        message: {
          ...event.data
        }
      })
    }
  })
  addScript('lucky.js', 'coverage-script')
}

addJSAndEventListener()

// 消息监听
chrome.runtime.onMessage.addListener((e, _, sendResponse) => {
  switch (e.action) {
    // 从 SW 发送消息到 content 脚本,获取 coverage 数据
    case 'fromSW2Content-getWindowData': {
      window.postMessage({
        type: 'get-coverage'
      })
      break
    }
    default:
      break
  }
  sendResponse()
  return false
})
  • addListener:消息监听,并进行 postmessage 消息传递给 lucky.js

4. lucky.js 文件

// sendCoverageData(true)
  • 因为我们是按钮点击在获取,所以可以把初始化就获取数据注释掉

5. service-worker.js

/**
 * service worker 监听 接收消息
 */
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  switch (message.action) {
    // 从 content 发送消息到 SW,发送数据
    case 'fromContent2SW-sendCoverage': {
      // 从 SW 发送消息到 popup,发送数据
      chrome.runtime.sendMessage({
        action: 'fromSW2Popup-sendCoverage',
        message: {
          ...message.message
        }
      })
      break
    }
    // 从 popup 发送消息到 SW,获取数据
    case 'fromPopup2SW-getWindowData': {
      // 从 SW 发送消息到 content,获取数据
      chrome.tabs.sendMessage(
        message.message.tabId,
        {
          action: 'fromSW2Content-getWindowData',
          message: {}
        },
        {},
        () => {}
      )
      break;
    }
    default: {
      break
    }
  }
  sendResponse()
  return false
})

  • 接收 popup 的消息,并发送到 content 文件
    • 需要 tabId 字段
  • 从 content 接收消息,并发送到 popup 页面
  • service-worker 对两者发送消息的方式不一样
    • content 因为是在页面中,所以需要 tabId
    • popup 则不需要 tabId

6. 效果展示

[外链图片转存中…(img-JdmxovqK-1734921003173)]

6.1. 小问题

多次点击会重复渲染

[外链图片转存中…(img-itJlga8y-1734921003173)]

我这里就不做处理,需要的话自己判断处理即可

四、总结

  • 获取逻辑和 《Chrome 浏览器插件获取网页 window 对象》中的方案一一样,有兴趣的可以试下其他方案
  • 我这是本地 iframe URL,如果你的 URL 是网页链接,可以在 index.js 中嵌入 lucky.js 做延迟处理
  • 如果你的 iframe URL 是动态的,比如,点击 tab,切换 URL,则可以在 index.js 中进行 MutationObserver 监听
  • 如果你需要部分内容嵌入 iframe 中,则可以使用 window.top === window.self 判断是否是顶层
  • 源码:【Gitee】
  • 🎉🎉🎉🎉🎉🎉

http://www.kler.cn/a/452098.html

相关文章:

  • 【系统架构设计师】真题论文: 论软件测试中缺陷管理及其应用(包括解题思路和素材)
  • 数字IC后端设计实现十大精华主题分享
  • LSTM-SVM时序预测 | Matlab基于LSTM-SVM基于长短期记忆神经网络-支持向量机时间序列预测
  • 前端文件下载多方式集合
  • 【Linux】进程间通信 -> 匿名管道命名管道
  • 【AIGC-ChatGPT进阶副业提示词】星际占卜师:探索星象能量的艺术【限时免费阅读,一天之后自动进入进阶课程】
  • 【ORB-SLAM3:相机针孔模型和相机K8模型】
  • Chapter 03 复合数据类型-1
  • RBF分类-径向基函数神经网络(Radial Basis Function Neural Network)
  • 数据库安全-redisCouchdb
  • 硬件设计-传输线匹配
  • 3D视觉坐标变换(像素坐标转换得到基于相机坐标系的坐标)
  • 以太网通信--读取物理层PHY芯片的状态
  • C++ 特殊类的设计
  • 开发微信小程序的过程与心得
  • RuoYi-ue前端分离版部署流程
  • mac中idea菜单工具栏没有git图标了
  • 【HarmonyOS NEXT】hdc环境变量配置
  • 认识计算机网络
  • CosyVoice安装过程详解
  • Java基础学习资料
  • Visual Studio - API调试与测试工具之HTTP文件
  • 《战神:诸神黄昏》游戏运行时提示找不到emp.dll怎么办?emp.dll丢失如何修复?
  • 前端开发 -- 自定义鼠标指针样式
  • 【pytorch】深度学习计算
  • 三相异步电动机不能起动有哪些原因