技術

CefSharp の IRequestHandler は最低限どう実装すればよいか

CefSharp の ChromiumWebBrowser でリクエストに関する処理をカスタムする場合、 CefSharp.IRequestHandler インターフェイスを実装したクラスのインスタンスを ChromiumWebBrowser の RequestHandler プロパティにセットする必要があります。

例えば、リンクを開く直前に移動先のURLによって許可したり拒否したりしたい場合は、 IRequestHandler のメソッド OnBeforeBrowse を実装すれば良いのですが、 IRequestHandler には他にも多数のメソッドが含まれるため、目的のメソッドを実装するだけでなく他のメソッドについても少なくとも最低限のものを実装しなければなりません。

各メソッドを最低限どう書けばよいかについては公式のサンプルソースに含まれるコードに答えがあるわけですが、その「答え」が「なぜそれで良いのか」についてはよく分からなかったので、ソースを読みながら自分なりの「最低限の答え」を導き出してみました。
といっても読んだのは主に IRequestHandler インターフェイスのソースに書かれているコメントだけです。

IRequestHandler のメソッド

答えの前にまずは各メソッドの簡単な説明を。
欲しいのは「最低限」ですので、メソッドが呼ばれるタイミングとメソッドの戻り値以外については特に触れません。

  • bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect);
    ブラウザのナビゲーションが行われる直前に呼ばれる。
    ナビゲーションをキャンセルする場合は true を、ナビゲーションを続行する場合は false を返す。
  • bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture);
    ナビゲーションによって新しいタブやウインドウが開かれるような場合に、 OnBeforeBrowse より先に呼ばれる。
    ナビゲーションをキャンセルする場合は true を、ナビゲーションを続行する場合は false を返す。
  • bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback);
    正しくないSSL証明書を持つページをリクエストした際に呼ばれる。
    ナビゲーションをキャンセルする場合は false を、ナビゲーションを続行する場合は true を返す。
  • void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath);
    プラグインがクラッシュした際に呼ばれる。
  • CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback);
    リソースへのリクエストが行われる前に呼ばれる。
    リソースの読み込みをキャンセルする場合は CefReturnValue.Cancel を、続行する場合は CefReturnValue.Continue を、非同期処理を用いつつ続行する場合は CefReturnValue.ContinueAsync を返す。
  • bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback);
    ブラウザがユーザーからの資格情報を必要とした際に呼ばれる。
    リクエストをキャンセルするには false を、続行する場合は true を返す。
  • void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status);
    レンダープロセスが予期せず終了した際に呼ばれる。
  • bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, Int64 newSize, IRequestCallback callback);
    JavaScript から webkitStorageInfo.requestQuota 関数によって明示的にクォータサイズを指定してストレージを要求された場合に呼ばれる。
    要求を拒否する場合は false を、許可する場合は true を返す。
  • void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, ref string newUrl);
    リソースの読み込みがリダイレクトされた際に呼ばれる。
  • bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url);
    不明なプロトコルのURLがリクエストされた時に呼ばれる。
    OSに登録されているプロトコルハンドラで処理する場合は true を、それ以外の場合は false を返す。
  • void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser);
    ブラウザに割り当てられたレンダービューがIPCメッセージを処理できるようになった際に呼ばれる。
  • bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response);
    リソースの応答を受け取った際に呼ばれる。
    リソースを普通に読み込む場合は false を、リダイレクトやリトライをする場合は true を返す。
  • IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response);
    リソースの応答内容をフィルタするために呼ばれる。
    レスポンスをフィルタする場合は IResponseFilter を実装したクラスのインスタンスを返す。それ以外の場合は null を返す。
  • void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength);
    リソースの読み込みが完了した際に呼ばれる。

全部で14個あります(-_-;)
ただし今日現在、 GetResourceResponseFilter は最新のソースには含まれてますが、NuGetの最新v47.0.3にはまだ含まれていないため、次の実装例には含めていません。

true を返すことでキャンセルを表現するメソッドもあれば、 false を返すことでキャンセルを表現するメソッドもあり、非常に分かりづらい感があります。
ですがよく調べると、 bool を返すメソッドでは false がデフォルト値になるよう敢えて真偽値の意味を変えてるようです。
であれば、とにかく false を返しておけばデフォルトの動作になるわけですからある意味分かりやすいともいえます。
(私が設計するときに真似したいとは思わないけど)

それとメソッドの名前が返り値について説明するようなものになってないのもつらいです。
ですがこれも、「メソッドのインターフェイス」ではなく「イベントハンドラのインターフェイス」と考えれば理解できます。
(私が設計するときはイベントハンドラをインターフェイスで定義しようとは思わないけど)

IRequestHandler を実装する最低限のクラス

以上を踏まえた「ぼくの考えた最弱の実装」です。
これだけでは何もしない意味のないクラスですが、リクエスト関連のカスタマイズを行うためのスタート地点となるクラスです。

using CefSharp;

namespace MyCefSharpTest
{
    class MyRequestHandler : IRequestHandler
    {
        bool IRequestHandler.OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
        {
            return false;
        }

        bool IRequestHandler.OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture)
        {
            return false;
        }

        bool IRequestHandler.OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback)
        {
            return false;
        }

        void IRequestHandler.OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath)
        {
        }

        CefReturnValue IRequestHandler.OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
        {
            return CefReturnValue.Continue;
        }

        bool IRequestHandler.GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
        {
            callback.Dispose();
            return false;
        }
        void IRequestHandler.OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status)
        {
        }

        bool IRequestHandler.OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize, IRequestCallback callback)
        {
            return false;
        }

        void IRequestHandler.OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, ref string newUrl)
        {
        }

        bool IRequestHandler.OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url)
        {
            return false;
        }

        void IRequestHandler.OnRenderViewReady(IWebBrowser browserControl, IBrowser browser)
        {
        }

        bool IRequestHandler.OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
        {
            return false;
        }

        void IRequestHandler.OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
        {
        }
    }
}

こうやってまとめて見ると、基本的に void なメソッドは空に、 bool なメソッドは false を返すように、 CefReturnValue なメソッドは CefReturnValue.Continue を返すようにしただけですね。
GetAuthCredentials だけは callback.Dispose(); の行が追加されてますが、これは公式のサンプルのソース中にそうするようにおすすめするとコメントが書かれていたためそうしてみました。

終わってみれば簡単な事ですが、最初は「これだけのメソッドどうやって実装するんだ…」と軽く途方に暮れました。
一つのメソッド(イベントハンドラ)をカスタムしたいだけなのに、多数のメソッドがひとつのインターフェイスにまとまってるがために全部実装しなければならないのは、どう実装するか分かった今でも面倒な感じがあります。

今回私は OnBeforeBrowse のカスタムを目的としていましたが、他のテクノロジであれば同じようなイベントがブラウザに用意されているため、簡単にカスタムできるんですよね。

ということで IRequestHandler を実装しようとしたらメソッドが多すぎて途方に暮れた方は是非参考にしてみてください。(あまり居ないかな?w)

参考: 他のテクノロジにおいて OnBeforeBrowse イベントに相当するイベント

テクノロジ ブラウザクラス イベント
Windows Forms System.Windows.Forms.WebBrowser Navigating
UWP Windows.UI.Xaml.Controls.WebView NavigationStarting
Geckofx Gecko.GeckoWebBrowser Navigating

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示

This site uses Akismet to reduce spam. Learn how your comment data is processed.