BlinkGTK API リファレンスマニュアル — 第 1 章 概観とライフサイクル

作成者 BlinkGTK Project
対象バージョン BlinkGTK 1.2.5-build3 (Chromium 156.0.8078.73)
位置づけ BlinkGTK を組み込むアプリを作る方向けのマニュアル。BlinkGTK を組み込んだ EPUB リーダーの運用で得た知見を含む

API の意味と動作は、公開ヘッダ blink_gtk/blink_gtk.h の記述が定めるものに従う。この章と食い違う場合はヘッダの記述を優先する。


1.1 BlinkGTK とは

BlinkGTK は、Chromium の Blink レンダリングエンジンを GTK4 ウィジェットとして
アプリケーションに組み込むためのライブラリである。WebKitGTK と同様の設計思想の
C API(BlinkWebView)を提供する。GTK4 アプリケーションは、ウェブコンテンツの
表示・ナビゲーション・JavaScript 連携・カスタム URI スキーム配信などを利用できる。
Chromium 本体をビルドする必要はない。 prebuilt パッケージ(共有ライブラリ +
ヘッダ + pkg-config)へリンクするだけでよい。本マニュアルは、BlinkGTK を組み込んだ EPUB リーダーを
実際に運用している立場から書かれている。

1.2 アーキテクチャ概観

階層は上から下へ次のとおり。

  1. 組み込む側のアプリ(あなたのアプリ) — GTK4 アプリケーション。ウィンドウ・メニュー等の
    chrome を GTK で構築し、その中に BlinkWebView を 1 つの子ウィジェットとして置く。
  2. BlinkWebView(BlinkGTK 公開 API 層) — GtkWidget のサブクラス(GObject)。
    ロードイベントのシグナル、カスタムスキーム、JS ブリッジ等の C API をここで受ける。
  3. Chromium(マルチプロセス) — Browser プロセス(あなたのアプリと同居)の下に
    Renderer / GPU 等の子プロセスが起動し、実際のレイアウト・描画を行う。

アプリが直接触るのは 2 層目までであり、Chromium のプロセスモデルは背後に隠れる。
ただし後述の「実運用の落とし穴」(サンドボックス、プロファイル、RUNPATH)は
このマルチプロセス構造に由来するため、存在だけは意識しておくとトラブル時に早い。

ヘッダとリンク

#include <blink_gtk/blink_gtk.h>
pkg-config --cflags --libs blinkgtk-0.1

include するのは blink_gtk.h だけです。

v1.2.0-build5 以前は、GObject 化以前 (2025 年) の内部ヘッダ blink_web_view.h
も同梱されていました。blink_gtk.h と同時に include すると同名 typedef が
別の型を指すため、確定的にコンパイルエラーになります。

error: conflicting types for 'BlinkWebView'; have 'struct BlinkWebView'

使えないヘッダを置いておく理由がないため、それ以降は同梱していません。
blink_gtk.h に無い機能が要る場合は Issue でご相談ください。

アプリケーションが include するのは blink_gtk/blink_gtk.h のみとすること。

v1.2.0-build6 以降は除外済みです (2026-09-03 に、当時公開されていた
devel パッケージをすべて展開して確認)。現在配られている
ヘッダは blink_gtk.h / blinkgtk_export.h / blinkgtk_version.h の 3 つです。

スレッド安全性ポリシー

全ての BlinkGTK 公開 API は GTK メインスレッドからのみ呼び出せる
(ヘッダ冒頭の Thread Safety Policy を参照)。コールバック
(JavaScript 結果、cookie、print 等)も常に GTK メインスレッドで呼ばれる。
ワーカースレッドからは g_idle_add() でメインスレッドへディスパッチする。

1.3 ライフサイクル全体像

BlinkGTK を組み込むアプリのプロセスは、次のフェーズを一方向に進む。

フェーズ 主な API 備考
(0) init 前設定 blink_gtk_set_resources_path() 等 blink_gtk_init() より前に呼ぶ必要がある
(1) 初期化 blink_gtk_init() 全 GTK/GLib 呼び出しより前・メインスレッドで 1 回
(2) WebView 生成 blink_web_view_new() / _new_with_gpu_mode() GtkWidget として返る。コンテナが要るなら自前の GtkOverlay / GtkBox に載せる
(3) ロード前登録 blink_web_view_register_custom_scheme_full()、register_message_handler()、g_signal_connect(load-changed) load_uri() より前に済ませる
(4) 配置・表示 gtk_window_set_child()、gtk_window_present() 通常の GTK4 ウィジェットとして扱う
(5) ロード blink_web_view_load_uri() 以後 load-changed シグナルで進行を受ける
(6) メインループ blink_gtk_run_main_loop() / blink_gtk_quit_main_loop() GTK ではなく Chromium の RunLoop
(7) 終了 blink_gtk_shutdown() run_main_loop() 復帰後(明示呼び出しを推奨。下記参照)

blink_gtk_shutdown() の要否とタイミング(BlinkGTK としての推奨)

呼び方 評価
run_main_loop() 復帰後に呼ぶ 推奨。teardown 順序が決定的になる(下記の最小完全例はこの形)
ウィンドウ close ハンドラ等 RunLoop 実行中に呼ぶ 安全。デッドロックせず遅延 shutdown に切り替わり、run_main_loop() 復帰後に実処理が走る
呼ばない Linux では動作はする(atexit の安全網が働く)が、teardown が atexit 任せで順序が非決定的。非推奨。Windows の 1.2.5-build1 までは、プロセスの終了に 20 秒以上かかり、最後に異常終了する

Linux で未呼出でも動いていたのは、ContentMainRunner::Run 成功後に atexit ハンドラが登録されており、
AtExitManager 生存中に自動 teardown されるため。blink_gtk_shutdown() を呼んでいない既存のアプリが
動いていた理由もこれ。Windows ではこの経路が終了の途中で異常終了するので、どの OS でも明示的に呼ぶ。

(0) init 前設定

以下は blink_gtk_init() の前に呼ぶことがヘッダで明示されている。

void blink_gtk_set_devtools_locale(const char* locale);   /* NULL=システムロケール追従 */
const char* blink_gtk_get_devtools_locale(void);
void blink_gtk_set_icu_data_path(const char* path);       /* icudtl.dat の絶対パス */
void blink_gtk_set_resources_path(const char* path);      /* pak/snapshot 群のルート */

通常はパッケージインストール先 <prefix>/lib/chromium/ が自動検出されるため
指定不要。非標準配置(自アプリへの同梱等)のときだけ set_resources_path() を
指定する。set_icu_data_path() と同時指定時は resources_path が優先される。

(1) 初期化

gboolean blink_gtk_init(int* argc, char*** argv);

argc/argv を渡すと Chromium 系コマンドラインフラグ(--no-sandbox 等)が
ここで解釈される。戻り値 FALSE は初期化失敗。メインスレッドで、他のあらゆる
GTK/GLib 呼び出しより前に 1 回だけ呼ぶ。

(2) WebView 生成

GtkWidget* blink_web_view_new(void);
GtkWidget* blink_web_view_new_with_gpu_mode(BlinkGpuMode mode);
GtkWidget* blink_web_view_new_container(void);
BlinkGpuMode blink_web_view_get_gpu_mode(BlinkWebView* web_view);
typedef enum {
  BLINK_GPU_MODE_SOFTWARE    = 0,  /* CPU 描画のみ(既定・GPU 不要) */
  BLINK_GPU_MODE_SWIFTSHADER = 1,  /* SwiftShader による GL エミュレーション */
  BLINK_GPU_MODE_EGL         = 2,  /* ネイティブ EGL ハードウェア GPU */
} BlinkGpuMode;

(3) ロード前登録

load_uri() を呼ぶ前に、コンテンツ側から発生するイベントの受け口を揃える。

/* カスタム URI スキーム(バイナリ対応版。画像・音声も途中で欠けずに返せる) */
void blink_web_view_register_custom_scheme_full(
    BlinkWebView* web_view, const char* scheme,
    BlinkCustomSchemeBytesCallback callback, gpointer user_data);

/* JS→C メッセージ(window.blinkgtk.postMessage) */
void blink_web_view_register_message_handler(
    BlinkWebView* web_view, const char* name,
    BlinkMessageCallback callback, gpointer user_data);

ロードイベントは GObject シグナル "load-changed" で受ける。

typedef enum {
  BLINK_LOAD_STARTED    = 0,
  BLINK_LOAD_COMMITTED  = 1,
  BLINK_LOAD_FINISHED   = 2,
  BLINK_LOAD_REDIRECTED = 3  /* 予約。現行ランタイムは発火しない */
} BlinkLoadEvent;

運用での注意: BLINK_LOAD_REDIRECTED は将来予約であり現行では発火しない。
また enum の整数値は、v1.0.10 のうち 2026-04-21 以降に配布したものから「実際に発火する値」に
合わせて明示された。それより前の版では、ヘッダの値と実際に発火する値がずれていて、
ev == BLINK_LOAD_FINISHED が黙って偽になった。古いパッケージのヘッダと新しいライブラリを混ぜないこと。

(4)〜(5) 配置とロード

BlinkWebView は普通の GTK4 ウィジェットとして gtk_window_set_child() で置き、
gtk_window_present() 後に blink_web_view_load_uri() を呼ぶ。

void blink_web_view_load_uri(BlinkWebView* web_view, const char* uri);
void blink_web_view_load_html(BlinkWebView* web_view, const char* html, const char* base_uri);

(6) メインループ

int  blink_gtk_run_main_loop(void);   /* 0=成功。復帰までブロック */
void blink_gtk_quit_main_loop(void);

g_application_run() ではなく Chromium の base::RunLoop を回す。GTK と
Chromium のライフサイクル衝突を避け、シャットダウン順序を制御するための設計で
ある。GLib の g_timeout_add() / g_idle_add() はこのループ下でも発火する
(筆者の EPUB リーダーでも、録画・診断のタイマに GLib timeout を常用している)。終了させたい箇所
(ウィンドウの close-request 等)から blink_gtk_quit_main_loop() を呼ぶ。

(7) 終了

void blink_gtk_shutdown(void);

run_main_loop() 復帰後、プロセス終了直前に呼ぶ。

1.4 最小完全例(GTK4 への載せ方)

実際に運用している EPUB リーダーの起動処理を 1 本に簡約した、コンパイル可能な例。カスタムスキーム app:// でローカルディレクトリを
配信し、ロード完了をシグナルで受ける。

/* minimal-embedder.c — BlinkGTK を組み込む最小のアプリ(GTK4) */
#include <blink_gtk/blink_gtk.h>
#include <gtk/gtk.h>
#include <string.h>

/* app://host/<path> をカレントディレクトリから配信(バイナリ可)。NULL=404 */
static GBytes* scheme_cb(BlinkWebView* wv, const char* uri,
                         char** out_mime, gpointer ud) {
  (void)wv; (void)ud;
  const char* p = strstr(uri, "://");
  if (!p) return NULL;
  const char* slash = strchr(p + 3, '/');           /* host の後の '/' */
  const char* rel = slash ? slash + 1 : "index.html";
  if (*rel == '\0') rel = "index.html";
  if (strstr(rel, "..")) return NULL;               /* path traversal 防御 */
  char* contents = NULL; gsize len = 0;
  if (!g_file_get_contents(rel, &contents, &len, NULL)) return NULL;
  if (out_mime && g_str_has_suffix(rel, ".html"))
    *out_mime = g_strdup("text/html");              /* NULL なら URI から推測 */
  return g_bytes_new_take(contents, len);
}

static void on_load_changed(BlinkWebView* wv, BlinkLoadEvent ev, gpointer ud) {
  (void)ud;
  if (ev == BLINK_LOAD_FINISHED)
    g_print("loaded: %s (title=%s)\n",
            blink_web_view_get_uri(wv), blink_web_view_get_title(wv));
}

static gboolean on_close(GtkWindow* w, gpointer ud) {
  (void)w; (void)ud;
  blink_gtk_quit_main_loop();
  return FALSE;
}

int main(int argc, char* argv[]) {
  /* (0) 非標準配置なら blink_gtk_set_resources_path() をここで。 */
  /* (1) 初期化(全 GTK/GLib 呼び出しより前) */
  /* (1b) 描画経路は init より前に決める。後から指定しても反映されない */
  blink_gtk_set_gpu_mode(BLINK_GPU_MODE_SOFTWARE);
  if (!blink_gtk_init(&argc, &argv)) return 1;

  /* (2) ウィンドウ + WebView */
  GtkWidget* window = gtk_window_new();
  gtk_window_set_default_size(GTK_WINDOW(window), 1024, 768);
  GtkWidget* webview = blink_web_view_new();

  /* (3) ロード前登録 */
  blink_web_view_register_custom_scheme_full(
      BLINK_WEB_VIEW(webview), "app", scheme_cb, NULL);
  g_signal_connect(webview, "load-changed", G_CALLBACK(on_load_changed), NULL);
  g_signal_connect(window, "close-request", G_CALLBACK(on_close), NULL);

  /* (4)〜(5) 配置・表示・ロード */
  gtk_window_set_child(GTK_WINDOW(window), webview);
  gtk_window_present(GTK_WINDOW(window));
  blink_web_view_load_uri(BLINK_WEB_VIEW(webview),
                          argc > 1 ? argv[1] : "app://local/index.html");

  /* (6)〜(7) メインループ → 終了 */
  int rc = blink_gtk_run_main_loop();
  blink_gtk_shutdown();
  return rc;
}

ビルドと起動:

export PKG_CONFIG_PATH="$BLINKGTK_PKG/lib/pkgconfig:$PKG_CONFIG_PATH"
cc -O2 -Wall -o minimal-embedder minimal-embedder.c \
   -Wl,-rpath,'$ORIGIN' \
   $(pkg-config --cflags --libs blinkgtk-0.1)

# 実行はランタイム(.so + chromium リソース)と同じディレクトリに置くのが確実
cp minimal-embedder "$BLINKGTK_PKG/lib/chromium/"
"$BLINKGTK_PKG/lib/chromium/minimal-embedder" --no-sandbox --no-zygote

-Wl,-rpath,'$ORIGIN' と「lib/chromium に置いて起動」の理由は次節。

1.5 実運用の落とし穴

初めて BlinkGTK を組み込む方が一度で動かせるよう、EPUB リーダーの運用で実際に起きたことを挙げる。

(a) RUNPATH / ライブラリ解決 — V8 snapshot mismatch で即座に異常終了

libblinkgtk.so は同一パッケージの Chromium リソース(v8_context_snapshot.bin
等)と厳密に対でなければならない。.pc 由来の絶対 rpath だけでビルドしたバイナリを、別のパッケージの
lib/chromium/ にコピーして起動しないでください。ビルド時パッケージの .so を
読み込んでしまいます。V8 snapshot mismatch で renderer が即座に異常終了します
(2026-07-07 に実際に確認)。対策は次のどちらか。

配布されたサンプルバイナリは RUNPATH がビルド時パスに焼き込まれていることが
あるため、パッケージを移動・差し替えて使う場合は自前ビルドが安全である。

実測では、--user-data-dir を渡してもプロファイルは分離されず、
~/.blink_gtk/ が同一ユーザーの全 BlinkGTK プロセスで共有される。さらに
ディスクへの永続化(localStorage 等)は SIGTERM などの正常終了経路でのみ
行われ、SIGKILL や _exit() では失われる。テストで状態を汚した場合は
~/.blink_gtk/ 配下の該当データを手で消す運用になる。複数アプリの同時運用や
プロファイル分離が要件なら、この挙動を前提に設計すること。

(c) --no-sandbox --no-zygote が必要な環境がある

Chromium サンドボックスが構成できない環境(コンテナ、user namespace 制限、
一部の開発環境)では、blink_gtk_init() に渡す argv に --no-sandbox --no-zygote
を含めないと子プロセスが起動できない。筆者の EPUB リーダーの起動スクリプトは常にこの
2 フラグ付きで起動している。環境変数での同等指定は第 9 章 BLINKGTK_NO_SANDBOX
を参照。セキュリティ上の含意(サンドボックス無効化)は理解の上で使うこと。

(d) GPU モードは software 推奨・プロセス全体で 1 つ

EGL(ハードウェア GPU)経路は実験的であり、筆者の EPUB リーダーは評価の結果「当面 software で
運用」すると決めている(縦書き EPUB 本文の描画・スクリーンショット採取とも
software で安定)。また前述のとおりモードはプロセス単位なので、WebView ごとの
切り替えはできない。

(e) オフスクリーンキャプチャは「見たまま」ではない

blink_web_view_capture_screenshot() 系は renderer 側で再描画したフレームを
保存するため、コンポジタ表示段の不具合(表示だけ白紙になる等)は写らないことが
ある。表示系の検証を capture PNG だけで合格と判定しないこと(実際にこれで
不具合を見逃したことがあり、今は GTK の合成結果を直接読む検査を併用している)。

1.6 バージョン確認

const char* blink_gtk_get_version(void);           /* 例 "1.2.1" */
const char* blink_gtk_get_chromium_version(void);  /* 例 "NNN.0.NNNN.N" */

/* build 番号 (Since 1.2.2)。build 番号は「同じ版を作り直した回数」です。
 * 同じ版でも build が違えば挙動が違うことがあります。 */
const char* blink_gtk_get_build(void);             /* 例 "2"。不明なら NULL */
const char* blink_gtk_get_version_full(void);      /* 例 "X.Y.Z-buildN"。必ず非 NULL */

コンパイル時マクロ(BLINKGTK_VERSION / CHROMIUM_VERSION)ではなく、実行時に
リンクされている .so から取得する(アプリを再ビルドしなくても、表示がライブラリの版に追従する)。
ログ・バグ報告には必ずこの実行時値を記録することを勧める。

1.7 他章への前方参照

(章題は予定です。今後変わることがあります)

本章の情報源

初稿は下記により起草した。その後 2026-09-03 と 2026-09-08 に、それぞれ
当時の最新の配布物で再確認
し、ヘッダ同梱の記述と版数の例を実体に合わせた。