BlinkGTK UI ハンドラ API リファレンス — ダイアログ・ファイル選択・証明書エラー

バージョン: 1.2.0-build2
最終更新: 2026-07-30
言語: 日本語 |

English


このページについて

ページが要求する ブラウザの UI をアプリが引き受けるための API です。
JavaScript の alert()、ファイル選択、証明書エラーの扱いを、アプリ自身の
ダイアログ・ポリシーで実装できます。

本ページはエンジン実装を実測して書かれています。ハンドラの配線状況は
1.2.0-build2 時点で再確認しました。ハンドラごとに
成熟度が異なるため、最初に対応状況を示します。

ハンドラ 現在の状態
JavaScript ダイアログ 動作します (制約: 同時に扱えるダイアログは 1 つ、beforeunload は届かない)
ファイル選択 動作します (制約: ディレクトリアップロード非対応)
証明書エラー 動作します (同期応答のみ・リクエストごとに毎回呼ばれる)
全画面 (Fullscreen API) 動作します (制約: 状態同期は片方向)
ダウンロード 未配線 — 登録できますが呼ばれません (現状と回避策)
HTTP 認証 未配線 — 登録できますが呼ばれません (現状と回避策)

旧版 (2026-07-29) からの訂正: 既定動作表に「ダウンロード →
~/Downloads/ に保存」「証明書エラー → 記録して続行」とありましたが、
どちらも誤りです。実際は「ダウンロード → 保存されない」「証明書エラー →
ブロック」です。下の表が実測に基づく正です。

まず知っておく 3 つの事実

  1. 全ハンドラは GTK メインスレッドで呼ばれます。 ハンドラ内から GTK
    API を直接呼んで構いません。ただしこのスレッドはエンジンのブラウザ UI
    スレッドでもあるため、ハンドラ内で長時間ブロックすると全 WebView の
    描画・入力・通信が止まります。
  2. ハンドラは WebView ごとに独立で、callbackNULL を渡すと
    解除です。user_data の寿命管理はアプリの責任です (GDestroyNotify
    引数はありません — 再登録・解除しても解放通知は来ません)。
  3. ハンドラに渡る文字列 (message / accept_types など) はハンドラから
    戻ると無効になります。
    非同期に使う場合は g_strdup() で複製して
    ください。

ハンドラ未設定時の既定動作 (実測)

ページの要求 ハンドラ未設定時の既定動作
alert() 表示されず、スクリプトはそのまま続行
confirm() 表示されず、false が返る
prompt() 表示されず、null が返る (空文字列ではない)
beforeunload 常に離脱を許可 (ハンドラを設定しても変わらない)
ファイル選択 (<input type="file">) キャンセル
ダウンロード 保存されない (開始直後に内部キャンセル。通知なし)
HTTP 認証 (401 / 407) キャンセル (サーバの 401 レスポンスがそのまま表示される)
証明書エラー ブロック (ページは白紙になり load-failed シグナルが発火)
全画面要求 (requestFullscreen()) ページ側は成功扱い (要素はビュー内で拡大) だが、ウィンドウは全画面化されない中途半端な状態になる — ハンドラの実装を推奨

一覧

したいこと API 状態
alert / confirm / prompt を自前 UI で blink_web_view_set_javascript_dialog_handler() 動作
ファイル選択を自前 UI で blink_web_view_set_file_chooser_handler() + blink_web_view_file_chooser_response() 動作
証明書エラーを判断する blink_web_view_set_certificate_error_handler() 動作
全画面要求に応答する blink_web_view_set_fullscreen_handler() + exit_fullscreen() / is_fullscreen() 動作
ダウンロード先を決める blink_web_view_set_download_handler() 未配線
HTTP 認証に応答する blink_web_view_set_auth_handler() + blink_web_view_auth_response() 未配線

JavaScript ダイアログ

alert() / confirm() / prompt() の要求を受け取ります。

シグネチャ:

typedef enum {
  BLINK_JS_DIALOG_ALERT = 0,
  BLINK_JS_DIALOG_CONFIRM = 1,
  BLINK_JS_DIALOG_PROMPT = 2,
  BLINK_JS_DIALOG_BEFORE_UNLOAD = 3,   /* 予約 — 現在は届かない (下記) */
} BlinkJSDialogType;

typedef void (*BlinkJSDialogResponseCallback)(gboolean success,
                                              const char* input_text,
                                              gpointer user_data);

typedef gboolean (*BlinkJSDialogRequestCallback)(
    BlinkWebView* web_view,
    BlinkJSDialogType dialog_type,
    const char* message,
    const char* default_prompt,
    BlinkJSDialogResponseCallback response_callback,
    gpointer response_user_data,
    gpointer user_data);

void blink_web_view_set_javascript_dialog_handler(
    BlinkWebView* web_view,
    BlinkJSDialogRequestCallback callback,
    gpointer user_data);

ハンドラは自前のダイアログを表示し、ユーザーの応答が決まったら
必ず response_callback を呼びます。非同期でかまいません —
ハンドラから TRUE を返してダイアログを表示し、ボタンが押されてから
response_callback を呼べます。

動作の詳細

観点 動作
スレッド GTK メインスレッド。GTK ダイアログを直接開いてよい
ページ側の待ち方 alert() 等は同期 IPC。応答するまでそのページの JavaScript は完全に停止する
応答しないと ページは永久に停止する。タイムアウトによる救済はない。さらに同じレンダラプロセスに属する他の WebView も入力を受け付けなくなる
message の中身 改行は \n に正規化されて届く (\r\n / \r\n になる)
呼び出し元の区別 どのフレームからの要求かは渡らない。iframe 内の広告の alert() も、メインページの alert() と区別できない。message にサイト名を含めない自前 UI だと、なりすまし表示に使われる余地がある
beforeunload ハンドラには届かない。エンジンが常に「離脱許可」で即応答するため、「このページを離れますか」の確認は現在実装できない (BLINK_JS_DIALOG_BEFORE_UNLOAD は将来のための予約値)
ダイアログの取り下げ ページ遷移・タブ破棄・レンダラクラッシュが起きても「ダイアログを閉じてよい」通知は来ない。アプリのダイアログは出たままになるため、load-changed シグナルで自前ダイアログを閉じる保険を入れておくとよい

制約: 同時に扱えるダイアログは 1 つ

response_callback の応答先は プロセス全体で 1 スロットしかありません。
1 つ目のダイアログに応答する前に 2 つ目の要求が来ると (別 WebView から、
または別 iframe から)、1 つ目の応答先が失われ、そのページは永久に停止
します
。手元に保持していた古い response_callback を呼ぶと、新しい方の
ダイアログに応答してしまいます。

実装例: GTK4 の非同期ダイアログで confirm() を引き受ける

typedef struct {
    BlinkJSDialogResponseCallback respond;
    gpointer respond_data;
} ConfirmCtx;

static void on_confirm_choice(GObject *src, GAsyncResult *res, gpointer data) {
    ConfirmCtx *ctx = data;
    int btn = gtk_alert_dialog_choose_finish(GTK_ALERT_DIALOG(src), res, NULL);
    ctx->respond(btn == 1, NULL, ctx->respond_data);
    g_free(ctx);
}

static gboolean on_js_dialog(BlinkWebView *view, BlinkJSDialogType type,
                             const char *message, const char *default_prompt,
                             BlinkJSDialogResponseCallback respond,
                             gpointer respond_data, gpointer user_data) {
    if (type != BLINK_JS_DIALOG_CONFIRM)
        return FALSE;                      /* alert / prompt は既定動作 */

    GtkAlertDialog *dlg = gtk_alert_dialog_new("%s", message);
    const char *buttons[] = { "キャンセル", "OK", NULL };
    gtk_alert_dialog_set_buttons(dlg, buttons);
    gtk_alert_dialog_set_default_button(dlg, 1);
    gtk_alert_dialog_set_cancel_button(dlg, 0);

    ConfirmCtx *ctx = g_new0(ConfirmCtx, 1);
    ctx->respond = respond;
    ctx->respond_data = respond_data;
    gtk_alert_dialog_choose(dlg, GTK_WINDOW(user_data), NULL,
                            on_confirm_choice, ctx);
    g_object_unref(dlg);
    return TRUE;
}

GtkAlertDialog は生成時に message を複製するため、ハンドラから戻った後も
表示は安全です。自前ウィジェットに表示する場合は g_strdup(message) して
ください。


ファイル選択

<input type="file"> の要求を受け取ります。

シグネチャ:

typedef gboolean (*BlinkFileChooserRequestCallback)(
    BlinkWebView* web_view,
    gboolean allow_multiple,
    const char* accept_types,
    gpointer user_data);

void blink_web_view_set_file_chooser_handler(
    BlinkWebView* web_view,
    BlinkFileChooserRequestCallback callback,
    gpointer user_data);

ハンドラで GtkFileDialog などを開き、選択が決まったら
blink_web_view_file_chooser_response() を呼びます。

accept_types の実際の形式

accept 属性の内容が MIME タイプ → 拡張子の順に並べ替えられ、全て
小文字化された
カンマ区切り文字列で届きます。属性の記述順は保存されません。

<input type="file" accept=".PNG, image/jpeg, .txt">

→ ハンドラに届く文字列: "image/jpeg,.png,.txt"

accept 属性がない場合は 空文字列 "" です (NULL は来ません)。
不正な要素 (MIME 形式でも .拡張子 形式でもないもの) は届く前に
除去されています。

シグネチャ:

void blink_web_view_file_chooser_response(BlinkWebView* web_view,
                                          const char** file_paths);

file_paths は NULL 終端の絶対パス配列です。配列と文字列は呼び出し時に
複製されるので、呼び出し後に解放して構いません。

const char *paths[] = { "/home/user/photo.jpg", NULL };
blink_web_view_file_chooser_response(view, paths);

動作の詳細と注意

観点 動作
パスの検証 一切行われません。存在しないパスでも change イベントは発火し、ページが読み取ろうとした時点で初めて失敗します。相対パスは事実上機能しません — 必ず絶対パスを渡してください
セキュリティ 応答したパスには、そのページからの読み取り権限が与えられます。ページが要求したファイル種別かどうか、ユーザーが本当に選んだファイルかどうかの確認はアプリの責任です
複数選択 allow_multipleFALSE のとき複数パスを返すと、単一選択のはずの <input> に複数ファイルが入った不正な状態になります。アプリ側で 1 個に絞ってください
ディレクトリ (webkitdirectory) 非対応です。ディレクトリ要求は allow_multiple = FALSE の通常要求と区別できない形で届き、応答してもページ側の webkitRelativePath が空になるため機能しません
ハンドラが FALSE を返したとき 要求はキャンセルされます。その後に file_chooser_response() を呼ばないでください (すでに応答済みの要求への二重応答となり、動作が未定義です)
応答しないままにすると その WebView のファイル選択が以後すべて即キャンセルになり、さらに window.open() もブロックされます。ダイアログを閉じる経路 (キャンセルボタン・Esc・ウィンドウクローズ) の全てで必ず response を呼んでください
ページ遷移後の応答 pending 中にページ遷移すると要求は内部で失効します。その後の response は静かに無視されます (クラッシュしません)

実装例: GtkFileDialog

static void on_file_open(GObject *src, GAsyncResult *res, gpointer data) {
    BlinkWebView *view = BLINK_WEB_VIEW(data);
    GFile *file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(src), res, NULL);
    if (file) {
        char *path = g_file_get_path(file);
        const char *paths[] = { path, NULL };
        blink_web_view_file_chooser_response(view, paths);
        g_free(path);
        g_object_unref(file);
    } else {
        blink_web_view_file_chooser_response(view, NULL);  /* キャンセル */
    }
}

static gboolean on_file_chooser(BlinkWebView *view, gboolean allow_multiple,
                                const char *accept_types, gpointer user_data) {
    GtkFileDialog *dlg = gtk_file_dialog_new();
    gtk_file_dialog_open(dlg, GTK_WINDOW(user_data), NULL, on_file_open, view);
    g_object_unref(dlg);
    return TRUE;
}

キャンセル経路 (file == NULL) でも必ず response を呼んでいる点が重要です。


証明書エラー

SSL 証明書エラーの続行可否を判断します。

シグネチャ:

typedef gboolean (*BlinkCertErrorCallback)(
    BlinkWebView* web_view,
    const char* url,
    const char* error_description,
    gpointer user_data);

void blink_web_view_set_certificate_error_handler(
    BlinkWebView* web_view,
    BlinkCertErrorCallback callback,
    gpointer user_data);

TRUE で続行、FALSE でブロックします。

既定 (ハンドラ未設定) は全ブロックです。自己署名証明書の社内サイトや
開発サーバを表示したい場合にだけ、ハンドラで明示的に TRUE を返します。
外部の任意サイトを表示する用途では、既定のままが安全です。

動作の詳細

観点 動作
応答方式 同期のみ。ハンドラの戻り値で即決定され、後から応答する API はありません。ハンドラ実行中は UI スレッドが止まるため、ダイアログを出してユーザーに聞く実装はできません — 許可リスト等の即時判定にしてください
呼ばれる頻度 エラーを起こすリクエストごとに毎回呼ばれます。判断は記憶されません。証明書が不正なホストから画像を 20 枚読むページでは 20 回呼ばれ得ます
url エラーを起こした個々のリソースの URL です。メインページとは限らず、サブリソース (画像・fetch) の場合はその URL が入ります。メインフレームかどうかを区別する手段は現在ありません
error_description net::ERR_CERT_AUTHORITY_INVALID のような Chromium net エラーの識別子文字列です。人間向けの説明文ではありません。証明書本体・有効期限・発行者は渡りません — 判断材料は実質 URL のホスト名です
ブロック時のページ Chrome のような警告ページは出ず、白紙になります。同時に load-failed シグナル (error_code は -200 番台の負値) が発火するので、自前のエラー画面はそちらで出します
TRUE の効き方 HSTS が有効なホストでも続行できてしまいます (Chrome では続行不能なケース)。TRUE を返す条件は最小限にしてください
届かないケース Service Worker 経由のリクエストの証明書エラーはハンドラに届かず常にブロックされます。また DevTools 接続中はツール側が判断を横取りする場合があります

実装例: ホスト許可リスト + 自前エラー画面

static gboolean on_cert_error(BlinkWebView *view, const char *url,
                              const char *error, gpointer user_data) {
    /* 社内の自己署名ホストだけ続行を許可。判断は同期で返す */
    gboolean allow = g_str_has_prefix(url, "https://intranet.example.jp/");
    g_message("cert error [%s] %s -> %s", error, url,
              allow ? "continue" : "block");
    return allow;
}

/* ブロックされたときの画面は load-failed 側で用意する */
static void on_load_failed(BlinkWebView *view, int error_code,
                           const char *failing_uri, gpointer user_data) {
    if (error_code <= -200 && error_code > -300) {   /* net の証明書エラー帯 */
        blink_web_view_load_uri(view, "app://error/cert.html");
    }
}

app:// の自前エラーページは [アプリ連携 API のカスタムスキーム]
(./app-integration-api-ja.md) で配信できます。load-failed のパラメータは
(error_code, failing_uri) の順です (Signals API)。


全画面 (Fullscreen API)

ページの requestFullscreen() / 全画面解除の要求を受け取ります。

シグネチャ:

typedef void (*BlinkFullscreenCallback)(BlinkWebView* web_view,
                                        gboolean enter_fullscreen,
                                        gpointer user_data);

void     blink_web_view_set_fullscreen_handler(BlinkWebView* web_view,
                                               BlinkFullscreenCallback callback,
                                               gpointer user_data);
void     blink_web_view_exit_fullscreen(BlinkWebView* web_view);
gboolean blink_web_view_is_fullscreen(BlinkWebView* web_view);

動作の詳細

観点 動作
ハンドラ未設定時 ページ側の requestFullscreen()成功扱いになります (拒否されません)。要素はビュー内いっぱいに広がりますが、GTK ウィンドウは全画面化されません — この中途半端な状態を避けるため、動画等を扱うアプリではハンドラの実装を推奨します
ハンドラの役割 enter_fullscreen に応じて gtk_window_fullscreen() / gtk_window_unfullscreen() を呼ぶのが基本形です
状態同期は片方向 エンジン → アプリの通知のみです。ユーザーが WM 側の操作で全画面を解除してもエンジンには伝わりません。アプリ側から解除するときは gtk_window_unfullscreen() だけでなく必ず blink_web_view_exit_fullscreen() を呼んでください (これを介すと Blink 側が解除され、ハンドラに enter_fullscreen=FALSE が届きます)
ESC キー エンジンは処理しません。アプリで実装します (capture フェーズのキーコントローラで ESC を拾い exit_fullscreen() を呼ぶ)
要求元の識別 iframe 由来かどうか等の情報は渡りません

実装例

static void on_fullscreen(BlinkWebView *view, gboolean enter,
                          gpointer user_data) {
    GtkWindow *win = GTK_WINDOW(user_data);
    if (enter)
        gtk_window_fullscreen(win);
    else
        gtk_window_unfullscreen(win);
}

/* ESC で解除 (エンジンは ESC を処理しないためアプリの責務) */
static gboolean on_key(GtkEventControllerKey *c, guint keyval, guint code,
                       GdkModifierType state, gpointer user_data) {
    BlinkWebView *view = BLINK_WEB_VIEW(user_data);
    if (keyval == GDK_KEY_Escape && blink_web_view_is_fullscreen(view)) {
        blink_web_view_exit_fullscreen(view);   /* Blink 側から正しく解除 */
        return TRUE;
    }
    return FALSE;
}

blink_web_view_set_fullscreen_handler(BLINK_WEB_VIEW(view), on_fullscreen, win);

ダウンロード (現状: 未対応)

このハンドラは現在も配線されておらず、登録しても呼ばれません。

typedef char* (*BlinkDownloadRequestCallback)(
    BlinkWebView* web_view,
    const char* url,
    const char* suggested_filename,
    const char* mime_type,
    gpointer user_data);

void blink_web_view_set_download_handler(
    BlinkWebView* web_view,
    BlinkDownloadRequestCallback callback,
    gpointer user_data);

実際の挙動:

配線はエンジン側の既知課題として管理しています。API シグネチャは配線後も
このまま使える予定なので、登録コードを書いておくこと自体は無害です
(呼ばれないだけです)。

回避策: アプリ管轄のダウンロードとして実装する

ファイルの保存が必要なアプリでは、ページ内 JS で取得してアプリに渡す
構成が現実的です (アプリ連携 API
JS ブリッジを利用):

/* ページ側: ファイルを取得して C 側へ送る (小さめのファイル向け) */
const buf = await (await fetch(fileUrl)).arrayBuffer();
window.blinkgtk.postMessage(buf);   /* ArrayBuffer はバイナリのまま届く */

C 側は message-received でバイトを受け取り、GFile 等で書き出します。
保存先の決定・上書き確認・進捗表示まで、アプリの UI として完全に制御
できます。


HTTP 認証 (現状: 未対応)

このハンドラも現在も配線されておらず、登録しても呼ばれません。
blink_web_view_auth_response() は現状、常に何もしません。

typedef gboolean (*BlinkAuthRequestCallback)(
    BlinkWebView* web_view,
    const char* url,
    const char* realm,
    gboolean is_proxy,
    gpointer user_data);

void blink_web_view_set_auth_handler(
    BlinkWebView* web_view,
    BlinkAuthRequestCallback callback,
    gpointer user_data);

void blink_web_view_auth_response(BlinkWebView* web_view,
                                  const char* username,
                                  const char* password);

実際の挙動: HTTP 401 / プロキシ 407 の認証要求は常に即キャンセル
され、サーバの 401 レスポンスボディがそのまま表示されます。

回避策は限定的です:

配線はダウンロードと合わせてエンジン側の既知課題として管理しています。


完全な例: confirm・ファイル選択・証明書ポリシーを引き受ける

動作するハンドラ 3 種だけを組み合わせた完全な例です。

#include <blink_gtk/blink_gtk.h>
#include <gtk/gtk.h>

typedef struct {
    BlinkJSDialogResponseCallback respond;
    gpointer respond_data;
} ConfirmCtx;

static void on_confirm_choice(GObject *src, GAsyncResult *res, gpointer data) {
    ConfirmCtx *ctx = data;
    int btn = gtk_alert_dialog_choose_finish(GTK_ALERT_DIALOG(src), res, NULL);
    ctx->respond(btn == 1, NULL, ctx->respond_data);
    g_free(ctx);
}

static gboolean on_js_dialog(BlinkWebView *view, BlinkJSDialogType type,
                             const char *message, const char *default_prompt,
                             BlinkJSDialogResponseCallback respond,
                             gpointer respond_data, gpointer user_data) {
    if (type != BLINK_JS_DIALOG_CONFIRM)
        return FALSE;                      /* alert / prompt は既定動作 */
    GtkAlertDialog *dlg = gtk_alert_dialog_new("%s", message);
    const char *buttons[] = { "キャンセル", "OK", NULL };
    gtk_alert_dialog_set_buttons(dlg, buttons);
    gtk_alert_dialog_set_default_button(dlg, 1);
    gtk_alert_dialog_set_cancel_button(dlg, 0);
    ConfirmCtx *ctx = g_new0(ConfirmCtx, 1);
    ctx->respond = respond;
    ctx->respond_data = respond_data;
    gtk_alert_dialog_choose(dlg, GTK_WINDOW(user_data), NULL,
                            on_confirm_choice, ctx);
    g_object_unref(dlg);
    return TRUE;
}

static void on_file_open(GObject *src, GAsyncResult *res, gpointer data) {
    BlinkWebView *view = BLINK_WEB_VIEW(data);
    GFile *file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(src), res, NULL);
    if (file) {
        char *path = g_file_get_path(file);
        const char *paths[] = { path, NULL };
        blink_web_view_file_chooser_response(view, paths);
        g_free(path);
        g_object_unref(file);
    } else {
        blink_web_view_file_chooser_response(view, NULL);
    }
}

static gboolean on_file_chooser(BlinkWebView *view, gboolean allow_multiple,
                                const char *accept_types, gpointer user_data) {
    GtkFileDialog *dlg = gtk_file_dialog_new();
    gtk_file_dialog_open(dlg, GTK_WINDOW(user_data), NULL, on_file_open, view);
    g_object_unref(dlg);
    return TRUE;
}

static gboolean on_cert_error(BlinkWebView *view, const char *url,
                              const char *error, gpointer user_data) {
    return g_str_has_prefix(url, "https://intranet.example.jp/");
}

int main(int argc, char **argv) {
    blink_gtk_init(&argc, &argv);

    GtkWidget *win = gtk_window_new();
    gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);

    GtkWidget *view = blink_web_view_new();
    gtk_window_set_child(GTK_WINDOW(win), view);

    blink_web_view_set_javascript_dialog_handler(BLINK_WEB_VIEW(view),
                                                 on_js_dialog, win);
    blink_web_view_set_file_chooser_handler(BLINK_WEB_VIEW(view),
                                            on_file_chooser, win);
    blink_web_view_set_certificate_error_handler(BLINK_WEB_VIEW(view),
                                                 on_cert_error, NULL);

    gtk_window_present(GTK_WINDOW(win));
    blink_web_view_load_uri(BLINK_WEB_VIEW(view), "https://example.com/");

    return blink_gtk_run_main_loop();
}

ビルド:

cc -o handlers-demo handlers-demo.c $(pkg-config --cflags --libs blinkgtk-0.1)

制限事項まとめ

項目 現状
ダウンロード 未配線。ダウンロードは無通知でキャンセルされる
HTTP 認証 未配線。401/407 は常にキャンセル。auth_response() は無効
beforeunload ハンドラに届かず、常に離脱許可
JS ダイアログの同時数 プロセス全体で 1 つ。2 つ目の要求で 1 つ目のページが停止する
ダイアログの取り下げ通知 ない。ページ遷移してもアプリのダイアログは残る
ディレクトリアップロード 非対応 (webkitRelativePath が空になる)
証明書エラーの非同期判断 不可 (同期戻り値のみ)。ユーザー確認ダイアログは出せない
証明書エラーの判断の記憶 されない。リクエストごとに毎回呼ばれる
要求元フレームの識別 不可 (JS ダイアログ・証明書エラーとも、メインフレームか iframe かは渡らない)
user_data の解放通知 ない (GDestroyNotify 引数なし)
GObject シグナル版 ない (本ページの 5 ハンドラは C 関数ポインタのみ。言語バインディングからは権限要求 = permission-request シグナルのみ利用可)

関連

変更履歴

バージョン 変更
v1.1.0 (2026-07-30 追補) 全画面 (Fullscreen API) ハンドラの節を新設 (従来どの doc にも実体解説がなかった)
v1.1.0 (2026-07-30) エンジン実装の実測に基づき全面改訂。既定動作表を訂正 (証明書エラー = ブロック / ダウンロード = 保存されない / prompt = null)。ダウンロード・HTTP 認証の未配線と回避策、JS ダイアログの同時 1 件制約、beforeunload 未実装、accept_types の実形式、応答忘れの帰結を明記
v1.1.0 (2026-07-29) 本ページ初版