リモート コンテンツの読み込みに加えて、コンテンツを WebView2 にローカルに読み込むこともできます。 ローカル コンテンツを WebView2 コントロールに読み込むには、次のようないくつかの方法があります。
- ファイル URL に移動する。
- HTML 文字列に移動する。
- 仮想ホスト名のマッピング。
-
WebResourceRequestedイベントを処理します。
詳細な内容:
- アプローチの選択
- ファイル URL に移動してローカル コンテンツを読み込む
- HTML 文字列に移動してローカル コンテンツを読み込む
- 仮想ホスト名マッピングを使用したローカル コンテンツの読み込み
- WebResourceRequested イベントを処理してローカル コンテンツを読み込む
- 関連項目
アプローチの選択
ローカル コンテンツを WebView2 コントロールに読み込むさまざまな方法では、次のシナリオがサポートされています。
| シナリオ | ファイル URL に移動する | HTML 文字列に移動する | 仮想ホスト名マッピングを使用する | 使用する WebResourceRequested |
|---|---|---|---|---|
| 配信元ベースの DOM API | はい ✔️ | いいえ ❌ | はい ✔️ | はい ✔️ |
| セキュリティで保護されたコンテキストを必要とする DOM API | いいえ ❌ | いいえ ❌ | はい ✔️ | はい ✔️ |
| 動的なコンテンツ | いいえ ❌ | はい ✔️ | いいえ ❌ | はい ✔️ |
| その他の Web リソース | はい ✔️ | いいえ ❌ | はい ✔️ | はい ✔️ |
| WebView2 プロセスで解決された追加の Web リソース | はい ✔️ | いいえ ❌ | はい ✔️ | いいえ ❌ |
これらのシナリオについては以下で説明します。
ファイル URL に移動してローカル コンテンツを読み込む
WebView2 を使用すると、ファイル URL へのナビゲーションと、基本的な HTML または PDF の読み込みが可能になります。 これは、ローカル コンテンツを読み込むための最もシンプルかつ効率的な方法です。 ただし、他のアプローチよりも柔軟性が低くなります。 Web ブラウザーと同様に、ファイル URL は一部の機能が制限されます。
文書には、そのファイル パスに固有の配信元があります。 つまり、
localStorageやindexedDBなどの配信元を必要とする Web API は機能しますが、保存されたデータを他のファイル パスから読み込んだ他のローカル ドキュメントでは使用できなくなります。一部の Web API は、セキュリティで保護された HTTPS URL のみに制限されており、ファイル URL で読み込まれたドキュメントでは使用できません。 これには、ビデオや音声を取得する
navigator.mediaDevices.getUserMedia()、デバイスの位置情報にアクセスするnavigator.geolocation.getCurrentPosition()、通知を表示するためのユーザーのアクセス許可を要求するNotification.requestPermission()などの API が含まれます。リソースごとに、フル パスを指定する必要があります。
ファイル URI から他のローカルファイルへの参照を許可したり、XSL 変換が適用された XML ファイルを表示したりするには、
--allow-file-access-from-filesbrowser 引数を設定できます。 CoreWebView2EnvironmentOptions.AdditionalBrowserArguments プロパティを参照してください。
ファイル URL に移動してローカル コンテンツを読み込む場合の考慮事項
ファイル URL はブラウザー内と同様に動作します。 たとえば、Web ページのコンテキスト内で作業していないため、ファイル URL で XMLHttpRequest (XHR) を作成することはできません。
すべてのリソースに対してファイルの完全パスを指定する必要があります。 例:
file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
クロスオリジン リソース
ファイル URL を指定すると、アプリはネットワーク上のドメインではなく、ディスク上のファイルに移動します。 その結果、結果のドキュメントでクロスオリジン リソースを使用できません。
配信元ベースの DOM API
ファイル URL を使用して読み込まれたドキュメントには、ブラウザーの場合と同様に、そのファイル パスに固有の配信元があります。
localStorage や indexedDB などの配信元を必要とする Web API は動作します。 ただし、異なるファイル URL から読み込まれた異なるドキュメントは、同じ生成元からのものとは見なされず、保存されている同じデータにアクセスすることはできません。
セキュリティで保護されたコンテキストを必要とする DOM API
一部の Web API は、セキュリティで保護された HTTPS URL のみに制限されており、ファイル URL で読み込まれたドキュメントでは使用できません。 これには、ビデオや音声を取得する navigator.mediaDevices.getUserMedia() 、デバイスの位置情報にアクセスする navigator.geolocation.getCurrentPosition() 、通知を表示するためのユーザーのアクセス許可を要求する Notification.requestPermission() などの API が含まれます。 詳細については、MDN の セキュアなコンテキストを参照してください 。
動的なコンテンツ
ファイル URL を使用してドキュメントを読み込む場合、ドキュメントのコンテンツはディスク上の静的ファイルから取得されます。 つまり、このローカル コンテンツを動的に変更することはできないということです。 これは、各応答を動的に生成できる Web サーバーからドキュメントを読み込む場合とは異なります。
その他の Web リソース
相対 URL 解決は、ファイル URL 経由で読み込まれたドキュメントでも機能します。 つまり、読み込まれたドキュメントは、CSS、スクリプト、画像ファイルなどの追加の Web リソースへの参照を持つことができます。これらのリソースもファイル URL 経由で提供されます。
WebView2 プロセスで解決された追加の Web リソース
ファイル URL は WebView2 プロセスで解決されます。 これは、ホスト アプリ プロセスの UI スレッドで解決される WebResourceRequested よりも高速なオプションです。
ファイル URL に移動してローカル コンテンツを読み込むための API
ファイル URL の例
このセクションでは、ローカル コンテンツ ファイル パスのファイル URL がどのように表示されるかを、プラットフォームに依存しない方法で示します。
WebView2 アプリは、 file:/// プレフィックスとスラッシュを使用してローカル ファイル URL をコード化する必要があります。 たとえば、Demo To Do の例の場合、パスは次のようになります。
file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
ローカル ファイルのプレフィックスが "file" の付いた完全パスをコピーするには、次のようにします。
必要に応じて、Demos リポジトリを複製して、ローカル コピーを作成します。 「Visual Studio Code 用の DevTools 拡張機能をインストールする」の「手順 5: デモ リポジトリを複製する」を参照してください。
Microsoft Edge で、 Ctrl+O キーを押してファイルを開きます。 ローカルで複製されたファイルなどのローカル
.htmlファイルを開きますDemos/demo-to-do/index.html:C:\Users\username\Documents\GitHub\Demos\demo-to-do\index.htmlアドレス バーには、最初は
file:///プレフィックスは表示されませんが、ドライブ文字で始まります。C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
アドレス バーをクリックして Home キーを押すか、 Ctrl+A キーを押してパス全体を選択します。
file:///を含むファイルパス全体がクリップボード バッファーにコピーされるため、file:///プレフィックスを含む完全なパスを貼り付けることができます。file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
関連項目:
- デモ To Do - レンダリングされたページ
- デモ To Do - ソース コード
- 手順 5: Visual Studio Code 用の DevTools 拡張機能のインストールでデモ リポジトリを複製します。
ファイル URL に移動する例
webView.CoreWebView2.Navigate(
"file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html");
HTML 文字列に移動してローカル コンテンツを読み込む
ローカル コンテンツを読み込むもう 1 つの方法は、 NavigateToString メソッドです。 この方法では、文字列から直接 WebView2 にコンテンツを読み込みます。 これは、アプリのコードを使用してコンテンツをパッケージ化する場合や、コンテンツを動的に作成する場合に役立ちます。
文字列への移動が役立つ別のシナリオは、URL からアクセスできないコンテンツを読み込む場合です。 たとえば、メモリ内表現の HTML ドキュメントがある場合は、 NavigateToString メソッドを使用してそのコンテンツを WebView2 コントロールに読み込むことができます。 これは、コントロールに読み込む前にコンテンツをファイルまたはサーバーに書き込む必要を回避したい場合に役立ちます。
HTML 文字列に移動してローカル コンテンツを読み込む場合の考慮事項
NavigateToString メソッドに渡される HTML コンテンツ文字列には、2 MB のサイズ制限があります。 文字列にインライン追加リソースが含まれている場合、このサイズ制限を簡単に超えてしまう可能性があります。 このサイズ制限を超えた場合は、"値が有効な範囲内にありません" というエラーが返されます。
配信元ベースの DOM API
NavigateToString メソッドを使用して読み込まれた文書は、[場所] が [about:blank] に設定され、[元] が [null] に設定されます。 つまり、 localStorage や indexedDB など、定義されている配信元に依存する Web API は使用できません。
セキュリティで保護されたコンテキストを必要とする DOM API
一部の Web API は、セキュリティで保護された HTTPS URL のみに制限されており、場所が about:blank に設定されているため、NavigateToString メソッド経由で読み込まれたドキュメントでは使用できません。 これには、ビデオや音声を取得する navigator.mediaDevices.getUserMedia() 、デバイスの位置情報にアクセスする navigator.geolocation.getCurrentPosition() 、通知を表示するためのユーザーのアクセス許可を要求する Notification.requestPermission() などの API が含まれます。 詳細については、MDN の セキュアなコンテキストを参照してください 。
動的なコンテンツ
NavigateToString メソッドを使用してローカル コンテンツを読み込む場合は、メソッドのパラメーターとしてコンテンツを直接指定します。 つまり、実行時にコンテンツを制御でき、必要に応じて動的に生成できます。
その他の Web リソース
NavigateToString メソッドを使用してローカル コンテンツを読み込んでも、結果のドキュメントで CSS、画像、スクリプト ファイルなどの追加の Web リソースを参照することはできません。 このメソッドでは、HTML ドキュメントの文字列内容のみを指定できます。 HTML ドキュメントから追加の Web リソースを参照するには、この記事で説明されている他の方法のいずれかを使用するか、HTML ドキュメントでそれらの追加の Web リソースをインラインで表します。
WebView2 プロセスで解決された追加の Web リソース
NavigateToString 前述のように、追加の Web リソースはサポートされていません。
HTML 文字列に移動してローカル コンテンツを読み込むための API
Web ページの文字列表現の例
Demo To Do Web ページの文字列表現を次に示します。 以下の一覧では、読みやすくするために改行が追加されています。 実際には、これらの行は 1 つの長い行に連結されます。
`<html lang="en"><head>\n
<meta charset="UTF-8">\n
<meta name="viewport" content="width=device-width, initial-scale=1.0">\n
<title>TODO app</title>\n
<link rel="stylesheet" href="styles/light-theme.css" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)">\n
<link rel="stylesheet" href="styles/dark-theme.css" media="(prefers-color-scheme: dark)">\n
<link rel="stylesheet" href="styles/base.css">\n
<link rel="stylesheet" href="styles/to-do-styles.css">\n
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📋
</text></svg>">\n
</head>\n\n
<body>\n
<h1>📋 My tasks</h1>\n
<form>\n
<div class="new-task-form" tabindex="0">\n
<label for="new-task">➕ Add a task</label>\n
<input id="new-task" autocomplete="off" type="text" placeholder="Try typing 'Buy milk'" title="Click to start adding a task">\n
<input type="submit" value="➡️">\n
</div>\n
<ul id="tasks"><li class="divider">No tasks defined</li></ul>\n
</form>\n\n \x3Cscript src="to-do.js">\x3C/script>\n \n\n
</body>
</html>`
上記の文字列を取得するには:
デモのTo Doに移動します。
Web ページを右クリックし、[ 検査] を選択して DevTools を開きます。
DevTools の コンソール で、「
document.body.parentElement.outerHTML」と入力します。 コンソールは、Web ページの文字列表現を出力します。
HTML 文字列に移動する例
// Define htmlString with the string representation of HTML as above.
webView.CoreWebView2.NavigateToString(htmlString);
仮想ホスト名マッピングを使用したローカル コンテンツの読み込み
WebView2 コントロールにローカル コンテンツを読み込むもう 1 つの方法は、仮想ホスト名マッピングを使用することです。 これには、ローカル ドメイン名をローカル フォルダーにマッピングすることが含まれます。これにより、WebView2 コントロールがそのドメインのリソースを読み込もうとすると、代わりに指定されたローカル フォルダーの場所からコンテンツが読み込まれます。 ドキュメントの元も仮想ホスト名になります。
この方法では、 CoreWebView2HostResourceAccessKind 列挙型を使用してクロスオリジン アクセスを指定できます。
現在の制限により、仮想ホスト名を使用してアクセスされるメディア ファイルの読み込みが遅くなる可能性があります。
仮想ホスト名マッピングを使用してローカル コンテンツを読み込む場合の考慮事項
配信元ベースの DOM API
仮想ホスト名マッピングを使用して読み込まれたローカル コンテンツは、HTTP または HTTPS の URL と対応する配信元を持つドキュメントになります。 つまり、 localStorage や indexedDB などの配信元を必要とする Web API が動作し、同じ配信元に属する他のドキュメントが、格納されたデータを使用できます。 詳細については、MDN の 同一生成元ポリシー を参照してください。
セキュリティで保護されたコンテキストを必要とする DOM API
一部の Web API は、セキュリティで保護された HTTPS URL のみに制限されます。 仮想ホスト名マッピングを使用すると、ローカル コンテンツの HTTPS URL が提供されます。 これは、ビデオや音声を取得する navigator.mediaDevices.getUserMedia() 、デバイスの位置情報にアクセスする navigator.geolocation.getCurrentPosition() 、通知を表示するためのユーザーのアクセス許可を要求する Notification.requestPermission() などの API が利用可能であることを意味します。 詳細については、MDN の セキュアなコンテキストを参照してください 。
動的なコンテンツ
仮想ホスト名マッピングを使用してローカル コンテンツを読み込む場合、仮想ホスト名を、ディスク上の静的ファイルを含むローカル フォルダーにマップします。 つまり、このローカル コンテンツを動的に変更することはできないということです。 これは、各応答を動的に生成できる Web サーバーからドキュメントを読み込む場合とは異なります。
その他の Web リソース
仮想ホスト名マッピングを使用して読み込まれるローカル コンテンツには、相対 URL 解決をサポートする HTTP または HTTPS URL があります。 つまり、読み込まれたドキュメントは、CSS、スクリプト、画像ファイルなどの追加の Web リソースへの参照を持つことができます。これらのリソースは、ソース マップを除き、仮想ホスト名マッピングを介して提供されます。以下の「 仮想ホスト名マッピングを使用したソースマップ」を参照してください。
WebView2 プロセスで解決された追加の Web リソース
仮想ホスト名の URL は WebView2 プロセスで解決されます。 これは、ホスト アプリ プロセスの UI スレッドで解決される WebResourceRequested よりも高速なオプションです。
仮想ホスト名マッピングを使用したソース マップ
コンパイル済みコンテンツのソース コードをデバッグするには、次のようなソース マップが必要です。
- トランスパイルされた JavaScript (TypeScript や縮小された JavaScript など)。
- SASS や SCSS などのコンパイルされた CSS
WebView2 は、仮想ホスト名マッピングを使用して読み込まれたコンテンツによって参照されるソース マップを読み込みません。
たとえば、WebView2 が仮想ホスト名マッピングを使用して main.js を読み込むとします。
main.js がソース マップとして main.js.map を参照している場合、main.js.mapは自動的に読み込まれません。
ソース マップを仮想ホスト名マッピングと共に使用するには、コンテンツのコンパイル中にインライン ソース マップを生成します。 インライン ソース マップは、対応するコンパイル済みファイル内に埋め込まれます。
仮想ホスト名マッピングを使用してローカル コンテンツを読み込むための API
仮想ホスト名のマッピング例
webView.CoreWebView2.SetVirtualHostNameToFolderMapping("demo",
"C:\Github\Demos\demo-to-do", CoreWebView2HostResourceAccessKind.DenyCors);
webView.CoreWebView2.Navigate("https://demo/index.html");
WebResourceRequested イベント処理によるローカル コンテンツの読み込み
WebView2 コントロールでローカル コンテンツをホストする別の方法は、 WebResourceRequested イベントに依存することです。 このイベントは、コントロールがリソースを読み込もうとしたときにトリガーされます。 このイベントを使用して、要求をインターセプトし、「 ネットワーク要求のカスタム管理」で説明されているように、ローカル コンテンツを提供できます。
WebResourceRequested 要求ごとにローカル コンテンツの動作をカスタマイズできます。 つまり、どの要求をインターセプトして独自のコンテンツを提供するか、またどの要求を WebView2 コントロールに通常どおりに処理させるかを決定できます。 ただし、動作をカスタマイズするには、仮想ホスト マッピングなどの追加のコードが必要であり、適切な応答を構築するために HTTP の知識が必要です。
WebView2 の観点からは、リソースはネットワーク経由で提供され、WebView2 は応答の一部としてアプリによって設定されたヘッダーに準拠します。 また、 WebResourceRequested イベントを使用すると、各要求に必要なクロスプロセス通信と処理が原因で、他の方法よりも低速になります。
カスタム スキームの登録
カスタム スキームを使用して、WebResourceRequested イベントを生成する Web リソース要求を作成する場合は、「WebView2 API の概要」の「カスタム スキームの登録」を参照してください。
WebResourceRequested イベントの処理によるローカル コンテンツの読み込みに関する考慮事項
配信元ベースの DOM API
WebResourceRequested を介して読み込まれたローカル コンテンツは、HTTP または HTTPS の URL と対応する配信元を持つドキュメントになります。 つまり、 localStorage や indexedDB などの配信元を必要とする Web API が動作し、同じ配信元に属する他のドキュメントが、格納されたデータを使用できます。 詳細については、MDN の 同一生成元ポリシー を参照してください。
セキュリティで保護されたコンテキストを必要とする DOM API
一部の Web API は、セキュリティで保護された HTTPS URL のみに制限されます。
WebResourceRequested を使用すると、HTTPS URL Web リソース要求を独自のローカル コンテンツに置き換えることができます。 これは、ビデオや音声を取得する navigator.mediaDevices.getUserMedia() 、デバイスの位置情報にアクセスする navigator.geolocation.getCurrentPosition() 、通知を表示するためのユーザーのアクセス許可を要求する Notification.requestPermission() などの API が利用可能であることを意味します。 詳細については、MDN の セキュアなコンテキストを参照してください 。
動的なコンテンツ
WebResourceRequested を使用してローカル コンテンツを読み込む場合は、イベント ハンドラーで読み込むローカル コンテンツを指定します。 つまり、実行時にコンテンツを制御でき、必要に応じて動的に生成できます。
その他の Web リソース
WebResourceRequested 相対 URL 解決をサポートする HTTP または HTTPS URL 経由で読み込まれるコンテンツを変更します。 つまり、結果のドキュメントでは、ソース マップを除き、WebResourceRequested 経由で提供される CSS、スクリプト、画像ファイルなどの追加の Web リソースへの参照を含めることができます。以下の「WebResourceRequested イベントを含むソース マップ」を参照してください。
WebView2 プロセスで解決された追加の Web リソース
ファイル URL または仮想ホスト名マッピングを使用してコンテンツを読み込む場合、解決は WebView2 プロセスで行われます。 ただし、 WebResourceRequested イベントはホスト アプリ プロセスの WebView2 UI スレッドで発生するため、結果のドキュメントの読み込みが遅くなる可能性があります。
- WebView2 は、イベントがホスト アプリ プロセスに送信されるのを待つために、まず Web ページの読み込みを一時停止します。
- WebView2 は、UI スレッドが使用可能になるまで待機します。
- WebView2 は、アプリ コードがイベントを処理するのを待ちます。
これには時間がかかる場合があります。
AddWebResourceRequestedFilterへの呼び出しを、WebResourceRequested イベントを発生させる必要がある Web リソースのみに制限してください。
WebResourceRequested イベントを使用したソース マップ
コンパイル済みコンテンツのソース コードをデバッグするには、次のようなソース マップが必要です。
- トランスパイルされた JavaScript (TypeScript や縮小された JavaScript など)。
- SASS や SCSS などのコンパイルされた CSS
WebView2 は、 WebResourceRequested イベントを使用して読み込まれたコンテンツによって参照されるソース マップを読み込みません。
たとえば、CoreWebView2WebResourceRequestedEventArgs の Response プロパティを設定して、WebResourceRequested イベント ハンドラーにmain.js読み込むとします。
main.js がソース マップとして main.js.map を参照する場合:
-
main.js.map自動的には読み込まれません。 -
WebResourceRequestedイベント ハンドラーは、main.js.mapを読み込むために再び呼び出されることはありません。
ソース マップを WebResourceRequested と共に使用するには、次のいずれかの方法を使用します。
コンテンツのコンパイル中にインライン ソース マップを生成します。 インライン ソース マップは、対応するコンパイル済みファイル内に埋め込まれます。
または、
WebResourceRequestedイベント ハンドラーで実行時にコンテンツに個別のソースをインラインでマップします。 この方法は、ビルド システムがインライン ソース マップをサポートしていない場合にのみ使用してください。
WebResourceRequested イベントを処理してローカル コンテンツを読み込むための API
WebResourceRequested イベントの処理例
// Reading of response content stream happens asynchronously, and WebView2 does not
// directly dispose the stream once it read. Therefore, use the following stream
// class, which properly disposes when WebView2 has read all data. For details, see
// [CoreWebView2 does not close stream content](https://github.com/MicrosoftEdge/WebView2Feedback/issues/2513).
class ManagedStream : Stream {
public ManagedStream(Stream s)
{
s_ = s;
}
public override bool CanRead => s_.CanRead;
public override bool CanSeek => s_.CanSeek;
public override bool CanWrite => s_.CanWrite;
public override long Length => s_.Length;
public override long Position { get => s_.Position; set => s_.Position = value; }
public override void Flush()
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
return s_.Seek(offset, origin);
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
int read = 0;
try
{
read = s_.Read(buffer, offset, count);
if (read == 0)
{
s_.Dispose();
}
}
catch
{
s_.Dispose();
throw;
}
return read;
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
private Stream s_;
}
webView.CoreWebView2.AddWebResourceRequestedFilter("https://demo/*",
CoreWebView2WebResourceContext.All);
webView.CoreWebView2.WebResourceRequested += delegate (object sender,
CoreWebView2WebResourceRequestedEventArgs args)
{
string assetsFilePath = "C:\\Demo\\" +
args.Request.Uri.Substring("https://demo/*".Length - 1);
try
{
FileStream fs = File.OpenRead(assetsFilePath);
ManagedStream ms = new ManagedStream(fs);
string headers = "";
if (assetsFilePath.EndsWith(".html"))
{
headers = "Content-Type: text/html";
}
else if (assetsFilePath.EndsWith(".jpg"))
{
headers = "Content-Type: image/jpeg";
} else if (assetsFilePath.EndsWith(".png"))
{
headers = "Content-Type: image/png";
}
else if (assetsFilePath.EndsWith(".css"))
{
headers = "Content-Type: text/css";
}
else if (assetsFilePath.EndsWith(".js"))
{
headers = "Content-Type: application/javascript";
}
args.Response = webView.CoreWebView2.Environment.CreateWebResourceResponse(
ms, 200, "OK", headers);
}
catch (Exception)
{
args.Response = webView.CoreWebView2.Environment.CreateWebResourceResponse(
null, 404, "Not found", "");
}
};
関連項目
- WebView2 API の概要で WebView2 に読み込まれたコンテンツを管理する
- デモの To Do レンダリングされたページ