読者です 読者をやめる 読者になる 読者になる

JavaFXのWebViewの検索を実現するのにもっと簡単な方法がありました

JavaFX

昨年の JavaFX Advent Calendar で次のようなエントリを書きました。 aoe-tk.hatenablog.com

このエントリでは、JavaFX の WebView を使ったアプリケーションに検索機能を実装する方法として、JavaScript のページ検索ライブラリを使う方法を紹介しています。
そこでは jQuery プラグインを使って実装していましたが、そのライブラリの CSSJavaScript オブジェクト定義が WebView で表示しているコンテンツのそれとバッティングする危険性がありました。

ところが、window.find() という非標準の関数があり、JavaFX の WebView がそれをサポートしていることを知りました。 *1
これを使えば特別なライブラリを読み込むこと無く、簡単に Web ページの検索機能を実現することができます。

以前の方法では、WebView 上のドキュメント読み込み完了時に検索のための jQuery プラグインの読み込みや、そのプラグインが要求する CSS クラスの定義の読み込みを行っていましたが、 window.find メソッドを使う場合はその必要はありません。何せ組み込みのメソッドなので。
件のエントリで解説した highlightPage() メソッドの実装を次のように変更します。

private static final String FIND_FUNCTION = "window.find(\"{0}\", false, false, true, false, true, false)";

private void highlightPage(Optional<String> word) {
    if (webEngine.getDocument() != null) {
        final String keyword = word.orElse("");
        if (!keyword.isEmpty()) {
            webEngine.executeScript(MessageFormat.format(FIND_FUNCTION, Encode.forJavaScript(keyword)));
        }
    }
}

単純に JavaScriptwindow.find メソッドを呼び出すように変更しています。
メソッドに渡す文字列に Encode.forJavaScript() というメソッドを噛ましていますが、これは OWASP Java Encoder Project というライブラリが提供する JavaScirpt 特殊文字をエスケープするための関数です。

また window.find メソッドは繰り返し呼ぶと、ページ内の次の一致箇所にフォーカスしてくれるので、次のように検索テキストフィールドのアクションイベントで highlightPage() メソッドを呼び出すようにし、検索フィールドにフォーカスがある状態で Enter キーを叩くと、次の一致箇所にフォーカスするようにしました。

<TextField fx:id="pageSearchBox" onAction="#handleSearchBoxAction" promptText="Find in page" HBox.hgrow="NEVER" />
@FXML
void handleSearchBoxAction(ActionEvent event) {
    highlightPage(Optional.ofNullable(pageSearchBox.getText()));
}

こうすると次のように検索フィールドにフォーカスがある状態での Enter キーをヒットすると、次の検索一致箇所にフォーカスが移動します。
f:id:aoe-tk:20150615001101p:plain

と言うわけで、JavaFX WebView で検索機能を実現する方法の訂正版でした。組み込みのメソッドを利用するので、この方法が一番良いと思われます。

実装の全体を確認されたい方は、私が開発している Social Bookmark Viewer FXBookmarkViewController.java 及び BookmarkView.fxml の実装を参照してください。

*1:この関数は元々 Netscape Navigator が独自に実装していた関数で、Firefoxレンダリングエンジンである Gecko もサポートし、WebKit もサポートしたようです。