WebView コンテンツ機能
ネイティブ WebView のダウンロード、ファイルドラッグ&ドロップ、初期化スクリプト、ズーム、印刷、Cookie。
セッション / ネットワーク設定(User-Agent、シークレットモード、プロキシ — 詳細はConfigurationを参照)に加えて、Murasaki は 6 つの WebView コンテンツ機能を提供します。ダウンロード、ファイルのドラッグ&ドロップ、信頼済みの初期化スクリプト、ページズーム、印刷、Cookie です。初期化スクリプトを除くすべての機能は、それぞれ専用の、デフォルトで拒否する webview:* ケイパビリティでゲートされています。
ダウンロード
webview:download を許可すると、ネイティブの with_download_started_handler がインストールされます。許可しない場合、すべてのダウンロードは拒否されます(デフォルトの挙動は変わりません)。提案されたファイル名は、可搬性のあるベース名へサニタイズされます(ディレクトリ部分、制御文字、先頭のドット、Windows で禁止されている文字 / 予約デバイス名、末尾のドットやスペースを処理し、UTF-8 で 240 バイトまでに制限します。空になった場合は download になります)。最終的なパスは、設定したディレクトリの内側に閉じ込められます — 名前が衝突した場合は、Wry 自身の挙動と同様に、拡張子の前に (n) が付与されます。
export default defineConfig({
// ...
webview: {
downloads: { directory: '/Users/example/Documents/MyApp Downloads' },
},
capabilities: ['webview:download'],
})downloads.directory は任意で、絶対パスである必要があります。省略した場合は、OS のユーザーダウンロードフォルダに解決されます。両方のライフサイクルイベントを 1 つの型付きヘルパーで購読できます。
import { subscribeDownloads } from 'murasaki'
const unsubscribe = subscribeDownloads((event) => {
if (event.type === 'started') console.log('downloading', event.url, event.path)
if (event.type === 'completed') console.log('done', event.success, event.path)
})started イベントと、その後に続く completed イベントを結び付ける信頼できる id はありません — Wry の completed ハンドラーが報告するのは url / path / success だけで、id は報告されないためです。そのため、同一 URL への同時ダウンロードは、completed だけからでは区別が曖昧になることがあります。macOS では、completed の path は常に null になります(上流の WebKit API の制約によるものです)。
ファイルドラッグ&ドロップ
webview:dragDrop を許可すると、ウィンドウにドラッグされたファイルのイベントを受け取れます。ネイティブハンドラーは常に OS のデフォルト処理を通します — 決してブロックしません — そのため、このケイパビリティの有無に関わらず <input type="file"> は動作し続けます。
import { useFileDrop } from 'murasaki'
function Dropzone() {
useFileDrop(({ paths }) => importFiles(paths))
return <div>Drop files here</div>
}drop に加えて enter / over / leave も使う場合は subscribeFileDrops を使ってください。
import { subscribeFileDrops } from 'murasaki'
const unsubscribe = subscribeFileDrops((event) => {
if (event.type === 'over') setHighlighted(true)
if (event.type === 'leave') setHighlighted(false)
})over は毎秒最大 20 回までスロットルされます — OS はドロップターゲットのハイライトを再配置するのに必要な頻度よりもずっと高い頻度でドラッグの移動を報告してくるためです。
export default defineConfig({
// ...
capabilities: ['webview:dragDrop'],
})信頼済みの初期化スクリプト
webview.initScripts は、すべてのページ読み込みの前に、プロジェクト側が書いた JavaScript を実行します — 設定側が所有する信頼済みのコードであるため、このページで扱う他の機能と異なり、ケイパビリティは不要です。パスはプロジェクトルートからの相対パスで指定し、内容は開発時 / バンドル時に読み込まれて埋め込まれ、宣言した順序で適用されます。
export default defineConfig({
// ...
webview: {
initScripts: ['scripts/polyfills.js', 'scripts/telemetry-bootstrap.js'],
},
})各ファイルは 256 KiB、合計では 1 MiB を上限とし、プロジェクトの読み込み時に検証されます(ファイルが見つからない場合やサイズを超過した場合は、murasaki dev / murasaki bundle コマンドが黙って無視するのではなく、明確なエラーで失敗します)。
ページズーム
webview.setZoom({ factor }) は、ページズームのレベルを設定します(0.25 から 5.0 まで)。webview:zoom がこの呼び出しをゲートします。
import { webview } from 'murasaki/native'
await webview.setZoom(1.25)ズームが使えるのは macOS 11 以降と iOS 14 以降のみです(古い macOS では呼び出しが黙って失敗し、reject された Promise として現れます)。Android では利用できません。別途 webview.hotkeysZoom(ケイパビリティではなく設定)は OS のズームホットキー / ジェスチャーを有効にします — 効果があるのは Windows(WebView2)のみで、macOS / Linux では何も行いません。
export default defineConfig({
// ...
webview: { hotkeysZoom: true },
capabilities: ['webview:zoom'],
})印刷
webview.print() は、現在のページに対してプラットフォーム標準の印刷ダイアログを開きます。webview:print が必要です。
import { webview } from 'murasaki/native'
await webview.print()find-in-page API はありません — Wry 側に該当する API が存在しないためです。上流に API が実装されるまでは対応範囲外とします。
Cookie
webview:readCookies は webview.getCookies() を、webview:writeCookies は webview.setCookie() と webview.deleteCookie() の両方をゲートします。
import { webview } from 'murasaki/native'
const { cookies } = await webview.getCookies({ url: 'https://example.com/' })
await webview.setCookie({
url: 'https://example.com/',
name: 'theme',
value: 'dark',
secure: true,
})
await webview.deleteCookie({ url: 'https://example.com/', name: 'theme' })getCookies() は最大 1000 件を返し、各 value は 4 KiB で切り詰められます。setCookie / deleteCookie は、Cookie 名を RFC 6265 のトークン文字集合で検証し、value を 4 KiB に制限し、url が http / https であることを要求します。deleteCookie は、名前・URL のホストをドメインとして、デフォルトの / パスで一致判定を行うため、デフォルト以外の path で設定された Cookie は deleteCookie から指定できません。
セキュリティ: ランタイム認証には Cookie を使用しません。defense in depth(多層防御)として、レガシーな予約名 murasaki_runtime は、この API から引き続き不可視かつ変更不可です。読み取り結果からは除外され、書き込み / 削除は、付与されたケイパビリティに関係なく構造化エラーで拒否されます。
Cookie へのアクセスには、構造化された URL スコープの使用を推奨します。スコープされた読み取りでは明示的な url が必須であり、URL なしの getCookies() は、レガシーな文字列形式の許可でのみ利用できます。書き込みは実際の Cookie パスと照合され、レンダラーは domain を使って親ドメインや兄弟ドメインへ Cookie を設定することはできません。
capabilities: [
{ permission: 'webview:readCookies', allow: { urls: ['https://app.example.com/**'] } },
{ permission: 'webview:writeCookies', allow: { urls: ['https://app.example.com/account/**'] } },
]export default defineConfig({
// ...
capabilities: ['webview:readCookies', 'webview:writeCookies'],
})ページ内の許可リクエスト
ページの JavaScript からの getUserMedia() / geolocation 呼び出しは、現時点では Murasaki によってインターセプトされません — 詳しい説明と macOS TCC との関係については、Securityを参照してください。