Skip to content

Conversation

@cyfung1031
Copy link
Collaborator

@cyfung1031 cyfung1031 commented Dec 9, 2025

概述 Descriptions

已测试在Chrome版和Firefox版都能够正常执行
后台脚本没有测试。
(VSCodeConnect以外的功能,Firefox MV3 能完全执行。可以正常在Firefox跑)

不会被浏览器截取。inject 要发一个 id key, content 要配合这个 key 发另一个沟通key (同步处理)
沟通key = uuidv5(inject/scripting发出的requestId, content的messageFlagId)
双方要用同一个沟通key才能对话

scripting在 scripting 分离环境直接透过 chrome.storage.onChange 取得 service worker 的broadcast 指示,再转发到 content & inject
这个机制减省了很多不必要的处理
(不用 tabs.query, 不用 tabs.sendMessage, 不用先传到 content 再传到 inject)
(不然的话,我浏览器有100个以上的tab, 每一次 valueUpdate 都要发 100次 sendMessage, 对 serviceWorker 本身处理影响很大)

scripting.js 无法像 inject 跟 content 把messageFlag封装到代码
而是一个通用脚本直接查 chrome.storage 的 messageFlag 再跟 content 请求对话
因为它是用来被动接收 service worker 的东西,所以不需要一个同步的messageFlag
不会影响 inject 和 content 的同步执行

同时,也解决了 Firefox MV3 中,runtime 没有 onMessage 的问题
(根据官方文档,这个看起来不像 Bug. userscriptAPI 的 runtime 就是只能发不能接收)

这个改动里, early-start 处理不变。
因为改的部份是 extServer 的接收处理。

非broadcast类的通讯,例如 emitEventToTab 那些有留著。
在 scripting 中接收然后转发。

变更内容 Changes

inject.js 和 content.js 也改动了,不再是一层套一层的 class 设计
代码不会重用。
直接写代码就好。方便理解和维护

为了方便使用 uuidv5, messageFlag 改成 uuidv4 格式

截图 Screenshots

@cyfung1031 cyfung1031 requested a review from CodFrm December 9, 2025 03:32
@cyfung1031 cyfung1031 force-pushed the develop/messaging-performance-boost2 branch from a08c0b8 to 2396bb5 Compare December 9, 2025 03:34
@cyfung1031 cyfung1031 changed the title 以类似broadcast机制重构通讯机制 [v1.3?] 以类似broadcast机制重构通讯机制 Dec 9, 2025
@CodFrm
Copy link
Member

CodFrm commented Dec 9, 2025

后台脚本没有测试

我测试了一下,没有发现问题

scripting在 scripting 分离环境直接透过 chrome.storage.onChange 取得 service worker 的broadcast 指示,再转发到 content & inject
这个机制减省了很多不必要的处理
(不用 tabs.query, 不用 tabs.sendMessage, 不用先传到 content 再传到 inject)
(不然的话,我浏览器有100个以上的tab, 每一次 valueUpdate 都要发 100次 sendMessage, 对 serviceWorker 本身处理影响很大)

这个消息投递的次数并没有消失,只是进行了转移,从我们的业务代码转移到了浏览器的内部机制,现在是真真正正的要投递触发100次 chrome.storage.local.onChanged.addListener 事件消息,同时有一次存储消耗,之前的逻辑反而可以根据实际的运行情况只推指定的tab,当然这是一个不错的思路,只是我觉得性能消耗要根据实际情况来看

不用先传到 content 再传到 inject

变成了先传到 scripting 再传到 inject,复杂度并没有消失,如果不是考虑firefox的话,我觉得不需要这一层

scripting.js 无法像 inject 跟 content 把messageFlag封装到代码
而是一个通用脚本直接查 chrome.storage 的 messageFlag 再跟 content 请求对话
因为它是用来被动接收 service worker 的东西,所以不需要一个同步的messageFlag
不会影响 inject 和 content 的同步执行

需要 查 chrome.storage 的 messageFlag 再跟 content 请求对话,early-start 的脚本,在极端情况下,可能丢失message

@CodFrm CodFrm added the P1 🔥 重要但是不紧急的内容 label Dec 9, 2025
@cyfung1031
Copy link
Collaborator Author

cyfung1031 commented Dec 9, 2025

这个消息投递的次数并没有消失,只是进行了转移,从我们的业务代码转移到了浏览器的内部机制

因为每一个 tab 有自己的 scripting
不是 serviceWorker 一个程序发出100个
而是每个 scripting 去处理
这是主要的分别吧

serviceWorker 是单线程。所有 scripts 的后台都是它在处理
尽量把工作能分散都分散处理吧

同时有一次存储消耗

这个还好吧。虽然更进一步是可以直接在 value 储存那边做。现在先这样。

之前的逻辑反而可以根据实际的运行情况只推指定的tab

valueUpdate 没有指定 tab
有指定tab的 emitEvent 还是保留原有的做法

当然这是一个不错的思路,只是我觉得性能消耗要根据实际情况来看

你拿 虚拟机 跑几百个脚本试试吧。应该能测试得出分别。

需要 查 chrome.storage 的 messageFlag 再跟 content 请求对话,early-start 的脚本,在极端情况下,可能丢失message

531ac10 这里有处理。
反正本身 valueChange 就不是同步
现在这个,不会丢失message,得到 messageFlag 后会补回事件
(messageFlag 改变的话,对话就会中断。需要重新载入。)


理想的话,应该是改成 UserScripts API Dynamic

UserScripts API Dynamic:
Uses the browser's UserScripts API to inject both the wrapper code and the userscript code.
The userscript is executed instantly -> document-start is supported.

这样的话后台API全部都经由 scripting 跟 service_worker 对话
不用多出一个 content.js 和 inject.js

但很可惜我不太懂如何改 rspack.config.ts 的设定,让它生成 wrapper 用的js, 然后跟脚本代码一同放在 userScripts API register 里

@CodFrm
Copy link
Member

CodFrm commented Dec 9, 2025

理想的话,应该是改成 UserScripts API Dynamic
这样的话后台API全部都经由 scripting 跟 service_worker 对话
不用多出一个 content.js 和 inject.js

不是很理解这个想法,为什么不用多出 content.js 和 inject.js,难道把 GM、沙盒之类的逻辑也放到 userscript 中?

另外注入的脚本可能是在页面环境中的(inject),也还是要由 content/scripting 转发消息给 service_worker

@cyfung1031
Copy link
Collaborator Author

cyfung1031 commented Dec 9, 2025

理想的话,应该是改成 UserScripts API Dynamic
这样的话后台API全部都经由 scripting 跟 service_worker 对话
不用多出一个 content.js 和 inject.js

不是很理解这个想法,为什么不用多出 content.js 和 inject.js,难道把 GM、沙盒之类的逻辑也放到 userscript 中?

另外注入的脚本可能是在页面环境中的(inject),也还是要由 content/scripting 转发消息给 service_worker

难道把 GM、沙盒之类的逻辑也放到 userscript 中?

对。
userscript 独自能跑
要用 GM API 时,scripting 没准备好的话,可以延迟发消息, scripting 准备好後再交由 scripting 发给 service_worker
(用 dispatchEvent + preventDefault 可以知道 scripting 是否有监听。没有的话 scripting 监听后再发出)
(虽然实际上 scripting 执行应总是优先于 userScripts )

这样就不用等 content.js 和 inject.js 的载入
直接自行做沙盒环境跑
( userscript API 所有都要使用 document-start 来建立沙盒。如果是 document-end 那些就等 body载入好跑 window['flag']

另外注入的脚本可能是在页面环境中的(inject),也还是要由 content/scripting 转发消息给 service_worker

对。转发消息用 dispatchEvent 根据 messageFlag 给 scripting
然后 scripting 再转发消息给 service_worker


脚本环境是 userScript API 的 main / content_script (没 chrome.storage)
转发消息的环境 是 scripting API 的 isolated ( 能跑 extension API. 包括 chrome.storage.local 存取)

@CodFrm
Copy link
Member

CodFrm commented Dec 9, 2025

那这样可能需要打包出一个 模板,service worker读取这个模板,然后将脚本代码放进去,说实话,不是很必要,这样有多少个脚本就要执行多少次初始化代码

TM 的 UserScripts API Dynamic 模式也是有 inject 和 content的

@CodFrm
Copy link
Member

CodFrm commented Dec 9, 2025

我进行了一次测试,storage的总耗时更长,可能因为有IO操作,实际的资源消耗在io操作上,但是接收端接受消息的时间更短


计时有误,storage的耗时都比 chrome.runtime.sendMessage 短,tab越多,差距越大,cpu用的performance.now,忽略掉这个,感觉也不好评估

image

@cyfung1031
Copy link
Collaborator Author

cyfung1031 commented Dec 9, 2025

我进行了一次测试,storage的总耗时更长,可能因为有IO操作,实际的资源消耗在io操作上,但是接收端接受消息的时间更短

计时有误,storage的耗时都比 chrome.runtime.sendMessage 短,tab越多,差距越大,cpu用的performance.now,忽略掉这个,感觉也不好评估

image

没代码 不太清楚实际测试方式

不过建议是,background 那边发一个消息,包含 timestamp
然后在接收那边计一下时间差,最后把时间差加起来做一个指标吧 ( $\sum_{i} (T_{r,i} - T_s)$ )
(尽量贴近后台 background 单线程 跟 多个前台执行环境的情况)
(tabs 那个如果有包含 tabs.query 就更好)

@CodFrm
Copy link
Member

CodFrm commented Dec 9, 2025

代码:归档.zip 里面的时间是消息到达时间

storage的耗时会短一些,但是cpu消耗这个不好评估

@CodFrm
Copy link
Member

CodFrm commented Dec 9, 2025

chrome.tab.query 耗时 1ms 左右,这个差距并不是很大 2x 的样子,但是实际情况会更复杂一些,valueUpdate并不算是广播,会根据实际情况去推送具体的tab,广播所有的tab都会去检查valueUpdate也是一个额外的消耗

我还是比较想保留原来的逻辑,没啥说服力,自己都说服不太了😅,chrome.storage 也可以接受吧

@cyfung1031
Copy link
Collaborator Author

cyfung1031 commented Dec 9, 2025

sc1067-test-code-v2.zip
sc1067-test-code-v3.zip

你拿这个跑跑看结果~
CPU时间那个,不用理会返回结果。 ScriptCat 也只是发出不理返回
预设跑100次。不然不够准
每次跑完后,后台部份要停一下 (20ms),不然一直堆,content_script 那边的处理追不上

看 SW 执行时间 和 latency 就好

例如我这边,23个 tabs ( 其他是未载入的)
结果是这样的
Screenshot 2025-12-10 at 1 45 18

@CodFrm
Copy link
Member

CodFrm commented Dec 10, 2025

image

不过我发现这种方式,页面中会有两个content,实际资源占用情况,我觉得还是不太好说

@cyfung1031
Copy link
Collaborator Author

不过我发现这种方式,页面中会有两个content,实际资源占用情况,我觉得还是不太好说

所以我才說, 把wrapper的部份直接塞到代碼腳本就好
反正沙盒環境也是獨立,互不相干,也是要獨立生成
而且沙盒環境跟inject/content本來就要一致

對話的部份是沒辨法,一定要有scripting /content script

但動態代碼都是userscripts api那邊搞
對話部份用靜態的就可以。直接scripting做


不過這些更進一步的改動可以之後處理
不用一步登天

@CodFrm
Copy link
Member

CodFrm commented Dec 10, 2025

所以我才說, 把wrapper的部份直接塞到代碼腳本就好 反正沙盒環境也是獨立,互不相干,也是要獨立生成 而且沙盒環境跟inject/content本來就要一致

對話的部份是沒辨法,一定要有scripting /content script

但動態代碼都是userscripts api那邊搞 對話部份用靜態的就可以。直接scripting做

不過這些更進一步的改動可以之後處理 不用一步登天

wrapper赛到脚本代码里面,那么每个脚本都要去处理沙盒和一些共用的东西,这样消耗的更多,始终还是要 inject 和 content 的,而且就算是塞进去,那也是另外一种概念上的 inject 和 content,你的脚本始终是要在这两个环境中运行的,不要考虑这个了

不考虑firefox的情况下,我感觉这个scripting还是意义不大,还是不太想引入scripting加大复杂度,光看性能测试,差距不算很大,而且还额外加入了一个页面的消耗,实际情况也不是会去给每个tab发的

或者只在firefox的环境下使用这种模式,chrome使用老的模式

// 避免使用版本号控制导致代码理解混乱
// 用来清除 UserScript API 里的旧缓存
const USERSCRIPTS_REGISTER_CONTROL = "92292a62-4e81-4dc3-87d0-cb0f0cb9883d";
const USERSCRIPTS_REGISTER_CONTROL = "92292a62-5e81-3dc3-87d0-cb0f0cb9883e";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://developer.chrome.com/docs/extensions/reference/api/userScripts?hl=zh-cn
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/userScripts#extension_updates

在扩展更新后 userScripts 是会清除的,这个实际上没什么作用,可以移除 (平常调试时重新加载扩展也会)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果是正常扩展版本更新,应该不一定会清除吧!?

Copy link
Member

@CodFrm CodFrm Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

会的,里面说了:您可以在扩展程序服务工作线程的 runtime.onInstalled 事件处理程序中运行代码,以重新添加它们。

扩展更新时会触发这个事件(打开更新日志就是依靠这个)

@CodFrm
Copy link
Member

CodFrm commented Dec 15, 2025

我看到了你在mozilla的提问了,本来想等他们回复再决定加不加scripting的,但是没信了?

还是不太想加入scripting,有额外的消耗,且加大了代码和维护的复杂度,至于考虑Firefox的兼容问题,我觉得再等一段时间看看吧,Firefox的mv3缺少好多东西

@cyfung1031
Copy link
Collaborator Author

我看到了你在mozilla的提问了,本来想等他们回复再决定加不加scripting的,但是没信了?

还是不太想加入scripting,有额外的消耗,且加大了代码和维护的复杂度,至于考虑Firefox的兼容问题,我觉得再等一段时间看看吧,Firefox的mv3缺少好多东西

还好吧。用 scripting 解决了, Firefox MV3 版就大致完成了
如果要用 chrome.storage.onChanged.addListener, 还是要 scripting

这个PR的scripting 是在异步协助跟service_worker的沟通
不会加大了代码和维护的复杂度 (你喜欢的还可以搞个什么 scriptingServer scriptingClient )
有额外的消耗是没错。但为了不用在 service_worker 用 tabs.sendMessage 每次valueUpdate 来来回回跑,我觉得是值得
chrome.storage.onChanged.addListener 的好处是,即使资讯量大&频密,浏览器自行分配资源处理,这样就不会影响前台的操作
相反 tabs.sendMessage 的优先度高,在多分页多脚本的操作下, service_worker 不断发消息的话,开支更大,影响前台(UI)的操作。

userScriptAPI 的 content 环境肯定是有限制
那么GM的API呀,跟后台相关的部份,就让 scripting 处理吧。
scripting 弹性大,直接能操作 storage, 日后也许不需要什么都发到 service_worker, scripting 处理也可以

@CodFrm
Copy link
Member

CodFrm commented Dec 17, 2025

我看到了你在mozilla的提问了,本来想等他们回复再决定加不加scripting的,但是没信了?
还是不太想加入scripting,有额外的消耗,且加大了代码和维护的复杂度,至于考虑Firefox的兼容问题,我觉得再等一段时间看看吧,Firefox的mv3缺少好多东西

还好吧。用 scripting 解决了, Firefox MV3 版就大致完成了 如果要用 chrome.storage.onChanged.addListener, 还是要 scripting

这个PR的scripting 是在异步协助跟service_worker的沟通 不会加大了代码和维护的复杂度 (你喜欢的还可以搞个什么 scriptingServer scriptingClient ) 有额外的消耗是没错。但为了不用在 service_worker 用 tabs.sendMessage 每次valueUpdate 来来回回跑,我觉得是值得 chrome.storage.onChanged.addListener 的好处是,即使资讯量大&频密,浏览器自行分配资源处理,这样就不会影响前台的操作 相反 tabs.sendMessage 的优先度高,在多分页多脚本的操作下, service_worker 不断发消息的话,开支更大,影响前台(UI)的操作。

userScriptAPI 的 content 环境肯定是有限制 那么GM的API呀,跟后台相关的部份,就让 scripting 处理吧。 scripting 弹性大,直接能操作 storage, 日后也许不需要什么都发到 service_worker, scripting 处理也可以

勉强可以接受,但是我一直想等firefox的回复,如果支持了在 userScripts 中使用 onMessage,我会毫不犹豫的砍掉 scripting 来保证架构简单

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1 🔥 重要但是不紧急的内容

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants