C# WebView2.ExecuteScriptAsync()のいくつかの使い方とDebug方法

先日はHTMLページのスクリプトとWebView2の間の通信について触れました。

WebView2を通じてWebサーバーなしでJavaScriptからローカルファイルを読み書き

HTMLページそのものは自分が作成したもので、よってスクリプトも自分の好きなようにHTMLファイルで指定できました。
しかし、もしページが自分のサイトでない場合、最初からページスクリプトの指定ができないため、拡張機能のようにContent Scriptをinjectしなければなりません。
WebView2はExecuteScriptAsyncメソッドを提供していますので、いくつかの基本的な使い方を試したいと思います。

目次

WebView2.ExecuteScriptAsyncメソッドの定義

Task<string> ExecuteScriptAsync (string javaScript);

現在WebView2に表示されているページにstring javaScriptをinjectして実行する。
実行の結果をawaitすればJSONエンコードされたstringが返される。

テスト環境の準備

  • Visual Studio Community 2022
  • WinForms プロジェクト@.Net 5
  • WebView2を初期化する
    async void InitWebViewAsync() {//WebView2を初期化する await wv2.EnsureCoreWebView2Async(null); }
  • WebView2にgoogle.comをテストに表示させる(ありがとうGoogle)
    const string URL = "https://www.google.com"; wv2.Source = new Uri(URL);
  • WebView2のNavigationCompletedイベント後であることを確認

WebView2.ExecuteScriptAsyncメソッドの使い方

1. Script文字列をそのまま実行、戻り値なし。例:<body>背景色を変更

  • C#コード
//Script文字列をそのまま実行、戻り値なし、bodyの背景色を変える await wv2.ExecuteScriptAsync("document.querySelector(\"body\").style.backgroundColor=\"lightgreen\"");
  • 実行結果:

    WebView2.ExecuteScriptAsync()で背景色変更

2. Script文字列をそのまま実行、戻り値あり。例:HTML取得

  • C#コード
//Script文字列をそのまま実行、戻り値あり、Googleのロゴ<img>のHTMLを取得する string html = await wv2.ExecuteScriptAsync("document.querySelector(\"img[alt='Google']\").outerHTML"); //JSONエンコードのstringから普通のstringに変換 html=System.Text.Json.JsonDocument.Parse(html).RootElement.GetString(); Debug.WriteLine("HTML:"); Debug.WriteLine(html);
  • 結果出力
HTML: <img class="lnXdpd" alt="Google" height="92" src="/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" srcset="/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png 1x, /images/branding/googlelogo/2x/googlelogo_color_272x92dp.png 2x" width="272" data-atf="1" data-frt="0">
  • ポイント
    ExecuteScriptAsyncで取得したHTMLのstringは"JSONエンコード"されたもので、ブラウザーが認識する普通のstringに変換が必要。

3. JavaScriptファイルを読み込んで実行、戻り値あり。例:<img>srcを取得

  • getSrc.jsファイル
getSrc(); function getSrc() { let img = document.querySelector("img[alt='Google']"); return img.src; }
  • C#コード
string scriptGetSrc = File.ReadAllText(".\\getSrc.js"); string src= await wv2.ExecuteScriptAsync(scriptGetSrc); Debug.WriteLine("src:"); Debug.WriteLine(src);
  • 結果出力
src: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"

4. JavaScriptファイルを読み込んで実行、戻り値あり、JS側パラメーターあり。例:C#で背景色を指定してJS側で変更

  • setBackColor.jsファイル
setBackColor(color); function setBackColor(c) { let body = document.querySelector("body"); debugger;//debugのため body.style.backgroundColor = c; return "bodyの色を" + c + "に設定しました。"; }
  • C#コード
// 戻り値あり、引数を必要とする関数をjsファイルから実行 Color randomColor = Color.FromArgb(new Random().Next(0, Int32.MaxValue)); string colorString = $"#{randomColor.R:X2}{randomColor.G:X2}{randomColor.B:X2}"; string scriptSetColor = File.ReadAllText(".\\setBackColor.js"); string result = string.Empty; if (isFirstRun)//isFirstRunは別途定義 { result = await wv2.ExecuteScriptAsync($"let color=\"{colorString}\";{scriptSetColor}"); isFirstRun = false; } else { result = await wv2.ExecuteScriptAsync($"color=\"{colorString}\";setBackColor(color);"); } Debug.WriteLine("result:"); Debug.WriteLine(result);
  • 結果出力
    <body>の背景色を変更したうえ、以下を出力:
result: "bodyの色を#BBE59Bに設定しました。"
  • ポイント
    ExecuteScriptAsyncの引数にlet color=\"{colorString}\";を足すことで、JS側にcolorを渡すことが可能です。
    ただし、一回実行すると、変数colorは既にメモリに存在するため、2回目以降も再度定義しようとするとエラーになります。
    よって、1回目の実行かどうかの判断をしたうえで、2回目以降なら、colorを定義するのではなく、値を変えるだけにします。

ExecuteScriptAsyncを使うときのDebug方法

  • 課題
    ExecuteScriptAsyncでinjectされたスクリプトはファイルでの存在ではないため、DevToolsのSourceタブで見つかりません。
    Break Pointを設定してDebugするのにまずDevToolsでJSコードを見つけ出す必要があります。
  • 方法1:JSコードにconsole.log();を入れてそこからさかのぼる
    例えばJSコードにconsole.log("I am here!");を入れたら、DevToolsのConsoleタブで以下のように表示されます。 そこでファイル名をクリックしたらinjectされたJSコードにたどり着きます、そこでbreak pointを設置したりできます。  
    DevToolsのConsoleでJSソースを確認する

  • 方法2 debugger文を使う
    上記4.の例のように、JSコードにdebugger文を入れれば、DevToolsが開いているときに自動的にそこでブレイクしますので、Console出力からさかのぼって探す必要がなくなります。 また、Call Stackからさらにコール元を追跡することができます。
debugger文でJSソースを特定する


感想

WebView2.ExecuteScriptAsyncメソッドは便利です。
簡単なJSコードでしたらそのまま実行し、ちょっと長いコードはVisual Studio Code等でまず書いてWebViewで読み込めばいいです。
その場合はVSCodeの自動完成や文法チェック機能も利用できますし、JSコードを少々変更してもC#アプリを再度コンパイルする必要もありません。
先日ブラウザーの拡張機能を作る記事を書きましたが、WebView2でも同じようなことができそうです。
Content Scriptはもちろん、C#側のWebView2でBackground.jsの役割を担えます。
さらに.Netのライブラリも使えますので、可能性が広がります。
今後はシナリオによってうまく使い分けできたらいいなと思います。

コメント

このブログの人気の投稿

C# 外部ライブラリを使わずに半角→全角カタカナ変換

C# WebView2を通じてWebサーバーなしでJavaScriptからローカルファイルを読み書き