接口简介
chrome.offscreen 是chrome 109以上开始提供的离屏 API 创建和管理离屏文档。使用该api需要在manifest中的Permissions声明 “offscreen”,该api可以实现不打开浏览器标签的情况下操作dom,实现mv3后台无法操作dom以及生命周期问题。
服务工作者没有DOM访问权限,许多网站都有限制内容脚本功能的内容安全策略。Offscreen API允许扩展在隐藏文档中使用DOM API,而不会通过打开新窗口或选项卡来中断用户体验。 runtime API是屏幕外文档支持的唯一扩展API。
清单
要使用Offscreen API,请在扩展清单中声明 “offscreen” 权限。举例来说:
{
"name": "My extension",
...
"permissions": [
"offscreen"
],
...
}
用法
作为屏幕外文档加载的页面与其他类型的扩展页面的处理方式不同。扩展的权限可以转移到屏幕外的文档,但对扩展API访问有限制。例如,由于 chrome.runtime API是屏幕外文档支持的唯一扩展API,因此必须使用该API的成员处理消息传递。
以下是屏幕外文档与正常页面行为不同的其他方式:
- 屏幕外文档的URL必须是绑定有扩展名的静态HTML文件。
- 屏幕外文档是 window 的实例,但其 opener 属性的值始终为 null 。
- 虽然一个扩展包可以包含多个屏幕外文档,但一个已安装的扩展一次只能打开一个。如果扩展在分割模式下运行,并且有一个活动的隐身配置文件,则正常配置文件和隐身配置文件都可以有一个离屏文档。
使用 chrome.offscreen.createDocument() 和 chrome.offscreen.closeDocument() 创建和关闭屏幕外文档。 createDocument() 需要文档的 url 、原因和理由:
chrome.offscreen.createDocument({
url: 'off_screen.html',
reasons: ['CLIPBOARD'],
justification: 'reason for needing the document',
});
参数原因(reasons)
有关有效原因的列表,请参阅原因部分。在文档创建期间设置原因以确定文档的寿命。 AUDIO_PLAYBACK 原因将文档设置为在30秒后关闭而不播放音频。所有其他的原因都没有设定寿命限制。
- “TESTING” 仅用于测试目的的原因。
- “AUDIO_PLAYBACK” 指定屏幕外文档负责播放音频。
- “IFRAME_SCRIPTING” 指定屏幕外文档需要嵌入iframe并为其编写脚本,以便修改iframe的内容。
- “DOM_SCRAPING” 指定屏幕外文档需要嵌入iframe并抓取其DOM以提取信息。
- “BLOBS” 指定屏幕外文档需要与Blob对象(包括 URL.createObjectURL() )交互。
- “DOM_PARSER” 指定屏幕外文档需要使用
- “USER_MEDIA” 指定屏幕外文档需要与来自用户媒体(例如 getUserMedia() )的媒体流进行交互。
- “DISPLAY_MEDIA” 指定屏幕外文档需要与显示媒体(例如 getDisplayMedia() )中的媒体流交互。
- “WEB_RTC” 指定屏幕外文档需要使用Web实时通信功能
- “CLIPBOARD” 指定离屏文档需要与之交互剪切板
- “LOCAL_STORAGE” 指定屏幕外文档需要访问localStorage
- “WORKERS” 指定屏幕外文档需要派生工作进程。
- “BATTERY_STATUS” 指定屏幕外文档需要使用navigator.getBattery。
- “MATCH_MEDIA” 指定屏幕外文档需要使用window.matchMedia。
- “GEOLOCATION” 指定屏幕外文档需要使用navigator.geolocation。
案例Examples
下面的示例说明如何确保存在屏幕外文档。 setupOffscreenDocument() 函数调用 runtime.getContexts() 来查找现有的屏幕外文档,或者如果文档不存在则创建文档。
let creating; // A global promise to avoid concurrency issues
async function setupOffscreenDocument(path) {
// Check all windows controlled by the service worker to see if one
// of them is the offscreen document with the given path
const offscreenUrl = chrome.runtime.getURL(path);
const existingContexts = await chrome.runtime.getContexts({
contextTypes: ['OFFSCREEN_DOCUMENT'],
documentUrls: [offscreenUrl]
});
if (existingContexts.length > 0) {
return;
}
// create offscreen document
if (creating) {
await creating;
} else {
creating = chrome.offscreen.createDocument({
url: path,
reasons: ['CLIPBOARD'],
justification: 'reason for needing the document',
});
await creating;
creating = null;
}
}
在向屏幕外文档发送消息之前,调用 setupOffscreenDocument() 以确保文档存在,如以下示例所示。
chrome.action.onClicked.addListener(async () => {
await setupOffscreenDocument('off_screen.html');
// Send message to offscreen document
chrome.runtime.sendMessage({
type: '...',
target: 'offscreen',
data: '...'
});
});
有关完整的示例,请参阅GitHub上的offscreen-clipboard和offscreen-dom演示。
在Chrome 116前后检查屏幕外文档是否打开的方法
runtime.getContexts() 是在Chrome 116中添加的。在早期版本的Chrome中,使用 clients.matchAll() 检查现有的屏幕外文档:
async function hasOffscreenDocument() {
if ('getContexts' in chrome.runtime) {
const contexts = await chrome.runtime.getContexts({
contextTypes: ['OFFSCREEN_DOCUMENT'],
documentUrls: [OFFSCREEN_DOCUMENT_PATH]
});
return Boolean(contexts.length);
} else {
const matchedClients = await clients.matchAll();
return await matchedClients.some(client => {
client.url.includes(chrome.runtime.id);
});
}
}
创建离屏页面 chrome.offscreen.createDocument
chrome.offscreen.createDocument({
url: chrome.runtime.getURL('offscreen/index.html'), // 你的离屏页面地址
reasons: ['DOM_PARSER'], // 扩展创建屏幕外文档的原因该接口需要实现什么参数参考上面 “参数原因(reasons)” 未正确声明会导致页面报错获取不到对应的权限,如未声明DOM_PARSER 则页面就获取不到document
justification: "使用的理由", // 开发人员提供的字符串,更详细地解释了背景上下文的必要性。用户代理可以在向用户显示时使用此。
})
closeDocument 关闭文档
chrome.offscreen.closeDocument(
callback?: function,
)