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.cspSourcehttps://*.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