本文目录
[[toc]]
vscode-resource 协议
基本格式为: vscode-resource:/Users/matt/projects/catre/creosote-cat.gif
由于自定义 URI Scheme 需要操作系统支持(添加到注册表),为了规避这种复杂行为,可以通过 service worker 拦截 https 请求,并解析到正确的资源地址,并且 service worker 还可以从他们处于活动状态的页面发送和接受消息。
整体的工作流程如下:
- 重写 HTML 中的
vscode-resource:为/vscode-resource - 注册 service worker ,拦截
/vscode-resource所有请求 - 为请求创建 Promise ,并创建缓存,在下次请求时优先使用缓存
- 在外部 WebView 环境中( iFrame 之外),通过 postMessage 将请求转发到 VSCode
- 在 VSCode 中处理资源请求,通过 postMessage 回传到 WebView 中
- 通过 postMessage 发送解析结果给 service worker
- service worker 对之前创建的 Promise resolve 并缓存请求结果
WebView
简单来说, WebView 提供了一个设置 HTML 以及通过 message 进行 host 与 view 交互的 API 。
主要有以下特点:
- 与主 VSCode 进程隔离
- 彼此孤立
- 控制本地资源加载(资源黑白名单,通过 vscode-resource 协议实现)
- 支持通过 postMessage 与 VSCode 通信
- 监控 WebView 内部的事件
- 禁用脚本
- 主题化
WebView 的内容将通过
data:URI 加载,这样可以确保每个 WebView 都是独立的源
WebView 整体结构如下:
<webview>
<html>
<body>
<!-- 注入事件监听劫持、主题样式、环境变量等等信息 -->
<script src="main.js"></script>
<script>
// 用于管理 WebView 内容的 VSCode 脚本
;(function () {
// 获取 uuid ,用于与 VSCode 进行通信的唯一标识
const id = document.location.search.match(/\bid=([\w\-]+)/)[1]
// 收集 iframe 中,对 VSCode 发送来的消息的监听函数
const handlers = new Map()
// 定义与 VSCode 通信的写函数
const postMessageToVsCode = (channel, data) => {
window.parent.postMessage({ target: id, channel, data }, '*')
}
window.addEventListener('message', (e) => {
if (e.data && (e.data.command === 'onmessage' || e.data.command === 'do-update-state')) {
// 处理内部 iframe 发送出来的消息,转发给 VSCode
postMessageToVsCode(e.data.command, e.data.data)
return
}
// 处理 VSCode 发来的请求
const channel = e.data.channel
const handler = handlers.get(channel)
if (handler) {
handler(e, e.data.args)
} else {
console.log('no handler for ', e)
}
})
// 处理 WebView 内容与 VSCode 通信问题的脚本
// 主要进行转发操作
createWebviewManager({
origin: document.location.href,
postMessage: (channel, data) => {
// 转发给 VSCode
postMessageToVsCode(channel, data)
},
onMessage: (channel, handler) => {
// 添加到监听函数收集
handlers.set(channel, handler)
},
})
})()
</script>
<iframe width="100%" height="100%" sandbox="allow-same-origin allow-scripts">
<!-- 实际上从扩展中加载的 WebView 内容 -->
<!-- 注入通信脚本,主要与 WebView 标签内的 Script 通信,由其转发到 VSCode -->
</iframe>
</body>
</html>
</webview>