如何在通过browser.scripting.executeScript执行的匿名脚本中访问browser对象并向扩展后台发送消息?
如何在通过browser.scripting.executeScript执行的匿名脚本中访问browser对象并向扩展后台发送消息?
嘿,你遇到的这个问题其实是浏览器上下文隔离导致的——你在MAIN世界注入的脚本是跑在网页自己的上下文里,而browser对象属于扩展专属的隔离上下文(也就是ISOLATED世界),所以在网页上下文里直接拿不到它,而且你也没法把browser对象作为参数传过去,因为它包含很多不能被序列化的方法和属性,跨边界传递会直接失效。
给你一个实用且符合安全规则的解决思路:
用自定义DOM事件做上下文桥接
这是最常用的方式,核心逻辑是让MAIN世界的脚本把拦截到的数据通过自定义事件发给ISOLATED世界,再由ISOLATED世界的脚本调用扩展API发消息到后台。
具体代码可以这么改:
首先,先在隔离上下文注入一个监听脚本,负责接收事件并转发消息:
// 先注入隔离上下文的监听脚本 await browser.scripting.executeScript({ target: details, world: "ISOLATED", injectImmediately: true, func: () => { // 监听来自MAIN世界的自定义事件 window.addEventListener("pushStateIntercepted", (e) => { const { state, unused, url } = e.detail; // 这里可以正常使用browser对象 browser.runtime.sendMessage({ type: "safari-onhistorystateupdated", state, unused, url }); }); } });
然后再注入你原来的MAIN世界拦截脚本,把原来调用browser.runtime.sendMessage的地方改成发送自定义事件:
// 注入MAIN世界的拦截脚本 await browser.scripting.executeScript({ target: details, world: "MAIN", injectImmediately: true, args: [tabId, frameId], func: (tabId, frameId) => { if (window.history.__pushState) { console.warn("Already injected"); return; } window.history.__pushState = history.pushState; console.log("Injected pushState()"); history.pushState = function(state, unused, url) { console.log("Intercepted pushState()", state, unused, url); window.history.__pushState(state, unused, url); // 创建自定义事件并传递数据 const event = new CustomEvent("pushStateIntercepted", { detail: { state, unused, url } }); window.dispatchEvent(event); }; } });
另外提醒一下,不要尝试用直接传递browser对象或者在MAIN世界调用chrome.runtime.sendMessage的方式——前者会因为序列化失效,后者属于非标准行为,不同浏览器兼容性差异很大,很容易出问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

