Open4
VSC拡張のWebViewで外部ページを表示する方法

概要
VSC拡張でWebViewを使う場合のTipsをまとめる。
共通
- 表示されない場合VSC開発ツールのログでエラー要因を確認できる

画像表示
外部サーバ提供
- httpsのサーバでリソースを提供する
- CSPのimg-srcに提供サーバのURLを指定する
- 自己署名認証のhttpsサーバではSimpleBrowserで表示されないのでおそらくNG(詳細未確認)
内部保持コンテンツ
- URIを
webview.asWebviewUri
で内部URIに変換する必要あり
→ 変換するとhttps://${ハッシュ文字列}.vscode-resource.vscode-cdn.net/...
に変換される - CSPに内部リソースのURI(
webview.cspSource
=https://*.vscode-cdn.net
)を追加 - HTMLのヘッダに
<base href="${内部URI}/">
を追加
スクリプト
インライン&外部サーバ
-
vscode.window.createWebviewPanel
の引数 options にenableScripts: true
を設定 - 表示ページのCSPに
script-src 'nonce-${nonce}';
を設定 - 表示ページのHTML<script> のパラメータに
nonce="${nonce}"
を追加(⚠️上記との差に注意) - サーバのAccess-Contorlを設定する
allow_credentials=True
-
Origin: vscode-webview://xxxxxx...
に対するaccess-control-allow-origin
を許可する(⚠️設定方法に注意)
置換例
contentData.replaceAll("<script", `<script nonce="${nonce}"`);
FastAPI allow-origin 設定例
app.add_middleware(
CORSMiddleware,
# Accept WebView requests
# allow_credentialsがTrueの場合は厳密な指定が必要で下記はNG
# allow_origins=["vscode-webview://"],
# 要求のOriginが可変のため正規表現で指定する
allow_origin_regex=r"^vscode-webview://.*$",
allow_methods=["*"],
allow_headers=["*"],
allow_credentials=True,
)
CSS
外部サーバ
- 先頭ページのCSPに styel-src を設定する
style-src ${webview.cspSource} ${BaseUri} vscode-webview:;
- サーバのAccess-Contorlを設定する(スクリプトと共通)

コードの例
webView.ts
const panel = vscode.window.createWebviewPanel(
_panel.viewType,
_panel.viewTitle,
column || vscode.ViewColumn.One,
{
enableScripts: true,
localResourceRoots: [vscode.Uri.joinPath(extensionUri, "media")],
}
);
const webview = this.panel.webview;
const mediaPath = vscode.Uri.joinPath(this.extensionUri, "media");
const mediaBaseUri = webview.asWebviewUri(mediaPath);
// Load the html file to display WebView.
const indexHtmlPath = vscode.Uri.joinPath(mediaPath, "index.html");
const htmlData = await vscode.workspace.fs.readFile(indexHtmlPath);
const htmlContent = new TextDecoder("utf-8").decode(htmlData);
// Use a nonce to only allow specific scripts to be run.
const nonce = getNonce();
// Add the CSP header and base URI to the HTML content.
const baseUriHeader = `<base href="${mediaBaseUri.toString()}/">`;
const cspHeader = `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}' 'unsafe-inline';">`;
const webviewContent = htmlContent
.replace("<head>", `<head>${baseUriHeader}${cspHeader}`)
.replace("<script>", `<script nonce="${nonce}">`);
panel.webview.html = webviewContent;

注意点
VSC拡張のプロジェクトをDevcontainerで開発している場合、「拡張機能」と「WebView」では実行している場所が異なります。
拡張機能 | WebView | |
---|---|---|
実行場所 | Devcontainer | ホストOS |