ハイライト
Safari 17 は Manifest v2/v3 を引き続きサポートし、Web 拡張は xrOS までカバー。コンテンツブロッカーに
:has()セレクタ、Declarative Net Request にリクエストヘッダー変更、App 拡張にサイト別権限、Private Browsing と Profiles での拡張挙動の制御が強化されました。
主要内容
クロスプラットフォーム Web 拡張
Safari 拡張には 4 種類あります。Content Blockers、Share Extensions、App Extensions、Web Extensions です。Safari 17 も 4 種類すべてをサポートしますが、Apple は Web Extensions をブラウザカスタマイズの未来と位置づけています。
Apple は W3C WebExtensions Community Group の共同議長を務め、他ブラウザベンダーと標準化を推進しています。1 つの Web Extension コードが iOS、iPadOS、macOS、xrOS で動作します。
xrOS の Web Extensions は iOS 版と同等の能力(スクリプト注入、バックグラウンドコンテンツ、ポップオーバー)を持ちます。
(00:45)
Safari 17 は Manifest v2 と v3 の両方をサポートしますが、新機能は v3 に追加され続けます。適切なタイミングで v3 へ移行してください。
コンテンツブロッカーの強化
コンテンツブロッカーは JSON ルールで宣言的にコンテンツをブロックまたは非表示にし、ユーザーが訪問したサイト情報へのアクセスは不要です。今年 :has() セレクタが追加されました。
:has() により子要素に基づいて親要素をマッチできます。例えば .paid-promo 子要素を含む .post を非表示にするなど、以前は純粋な CSS セレクタでは難しかった処理が可能です。
(02:50)
Declarative Net Request の更新
Declarative Net Request は Web 拡張が宣言的にネットワークリクエストをインターセプト・変更できます。Safari がシステムレベルでルールを実行し、省電力とプライバシーを両立します。
Safari 16.4 からリクエストヘッダーの変更(設定、追加、削除、完全除去)に対応しました。
この機能には manifest で declarativeNetRequestWithHostAccess 権限の宣言が必要で、拡張はサイト別の許可を得て初めてルールが有効になります。
(03:37)
新 API declarativeNetRequest.setExtensionActionOptions で、バッジテキストを操作回数(ブロック数など)の自動表示に設定できます。閲覧履歴を秘匿したまま、拡張の動作をユーザーに示せます。
(05:09)
スクリプトとストレージ API
registerContentScript 系 API でコンテンツスクリプトをプログラム的に登録・更新・削除できます。manifest の静的宣言を補完し、特定ページや条件に応じて動的注入でき、登録はセッションをまたいで永続化されます。
(05:45)
Safari 16.4 で session storage 領域が追加されました。local storage と異なりメモリ上に保持され、Safari 終了時に消去されます。復号キーや認証トークンなど機密データに適しています。
(06:22)
サイト別権限
Safari App Extensions がサイト別許可に対応し、Web 拡張の権限モデルと統一されました。拡張を初めて有効化した時点ではサイトアクセス権はありません。ページアクセスを試みるとツールバーアイコンにプロンプトが表示され、「1 日許可」または「常に許可」を選べます。
許可後はツールバーアイコンが着色表示され、権限はいつでも取り消せます。
旧 Safari からアップグレードするユーザーは既存権限が移行され、「各 Web サイトで確認」でプライバシーを強化するバナーも表示されます。
(07:24)
すべての拡張はデフォルトでツールバーアイコンを表示します。正しい着色のため PDF ベクターアイコンの提供を推奨します。
Private Browsing と Profiles
Safari 17 では、Private Browsing ウィンドウにアクセスできる拡張を個別に制御でき、他の閲覧シーンで拡張を無効にする必要はありません。
スクリプト注入やページ読み取りを行う拡張は Private Browsing ではデフォルトでオフ。ページ内容にアクセスしないコンテンツブロッカーなどは自動許可されます。
Profiles は今年 Safari の新機能で、履歴、Cookie、サイトデータを分離します。各 Profile に独立した拡張設定があり、Profile 内で有効化すると新しい UUID、バックグラウンドページ、ストレージを持つ別インスタンスになります。サイト別権限は Profile 間で共有されます。
(09:02)
ネイティブホスト App と通信する拡張は、複数 Profile からのメッセージを処理する必要があります。beginRequest(with context:) で userInfo をデコードし、Profile 実行時は SFExtensionProfileKey の profileIdentifier を確認します。
Safari 17 の Develop メニューでは、Web Extension のバックグラウンドコンテンツが拡張ごとにグループ化され、各 Profile の inspect 可能なバックグラウンドページと service worker が一覧表示されます。
詳細
コンテンツブロッカーの :has() ルール
[
{
"action": {
"type": "hide"
},
"trigger": {
"url-filter": ".*",
"if-domain": ["example.com"],
"selector": ".post:has(.paid-promo)"
}
}
]
キーポイント:
"type": "hide"はネットワークをブロックせず要素を非表示"url-filter": ".*"はすべての URL にマッチ"if-domain"で指定ドメインに限定"selector": ".post:has(.paid-promo)"は.paid-promo子要素を含む.postにマッチ:has()はネストや他セレクタとの組み合わせをサポート
Declarative Net Request によるヘッダー変更
manifest.json で権限を宣言:
{
"manifest_version": 3,
"permissions": [
"declarativeNetRequest",
"declarativeNetRequestWithHostAccess"
],
"host_permissions": [
"*://example.com/*"
]
}
キーポイント:
- ヘッダー変更とリダイレクトには
declarativeNetRequestWithHostAccessが必要 host_permissionsでアクセス可能ドメインを宣言- ユーザーがサイト別許可を与えて初めてルールが有効
rules.json のルール定義:
[
{
"id": 1,
"priority": 1,
"action": {
"type": "modifyHeaders",
"requestHeaders": [
{
"header": "User-Agent",
"operation": "set",
"value": "MyExtension/1.0"
},
{
"header": "X-Custom-Header",
"operation": "append",
"value": "extra-value"
},
{
"header": "X-Tracking-Id",
"operation": "remove"
}
]
},
"condition": {
"urlFilter": "||example.com",
"resourceTypes": ["main_frame"]
}
}
]
キーポイント:
"operation": "set"は既存値を置換"operation": "append"は既存値に追記"operation": "remove"はヘッダーを完全削除"resourceTypes"で適用リソース種別を限定
バッジカウント設定:
// Safari 16.4+
chrome.declarativeNetRequest.setExtensionActionOptions({
displayActionCountAsBadgeText: true
});
キーポイント:
displayActionCountAsBadgeText: trueで操作回数をバッジに自動表示- 現時点ではこの API の唯一のオプション
- ルール発火回数に基づき自動更新
プログラム的コンテンツスクリプト登録
await chrome.scripting.registerContentScripts([
{
id: "webkit-highlight",
matches: ["https://webkit.org/*"],
js: ["highlight.js"],
runAt: "document_idle",
persistAcrossSessions: true
}
]);
await chrome.scripting.updateContentScripts([
{
id: "webkit-highlight",
matches: ["https://webkit.org/*", "https://bugs.webkit.org/*"]
}
]);
await chrome.scripting.unregisterContentScripts({ ids: ["webkit-highlight"] });
キーポイント:
registerContentScriptsで動的登録persistAcrossSessions: trueでセッション跨ぎ永続化updateContentScriptsで既存設定を変更unregisterContentScriptsで ID 指定削除- manifest の静的
content_scriptsを補完
Session Storage の利用
await chrome.storage.session.set({
authToken: "eyJhbGciOiJIUzI1NiIs...",
decryptionKey: "base64-encoded-key"
});
const data = await chrome.storage.session.get(["authToken"]);
console.log(data.authToken);
await chrome.storage.local.set({ settings: { theme: "dark" } });
await chrome.storage.session.set({ tempKey: "value" });
キーポイント:
chrome.storage.sessionは Safari 16.4 で追加- メモリ上に保持し、ディスクには書き込まない
- Safari 終了時に自動消去
- 認証トークンや復号キーなど機密データ向け
local/syncと同じ API
SVG アイコン設定
{
"manifest_version": 3,
"icons": {
"16": "icon.svg",
"32": "icon.svg",
"48": "icon.svg",
"128": "icon.svg"
}
}
キーポイント:
- Safari 16.4 以降 SVG アイコンをサポート
- 1 つの SVG で全サイズの PNG を代替可能
- Safari がスケーリングを自動処理
- 正しい着色のためベクターアイコンを推奨
Profile 対応メッセージ処理
import SafariServices
class ExtensionRequestHandler: NSObject, NSExtensionRequestHandling {
func beginRequest(with context: NSExtensionContext) {
guard let item = context.inputItems.first as? NSExtensionItem,
let userInfo = item.userInfo else { return }
if let profileId = userInfo[SFExtensionProfileKey] as? String {
handleRequest(forProfile: profileId, context: context)
} else {
handleRequest(context: context)
}
}
}
キー�ポイント:
SFExtensionProfileKeyで実行中 Profile を識別profileIdentifierは各 Profile の一意 ID- Profile ごとに独立した UUID、バックグラウンドページ、ストレージ
- ネイティブホスト App は
profileIdentifierでデータを分離
重要ポイント
1. :has() セレクタで精密なコンテンツブロック
- やること:コンテンツブロッカールールで
:has()を使い、URL だけでなくページ構造に基づいて非表示 - 価値:「広告ラベル付き記事カード」など複雑なケースを誤ブロック少なく実現
- 始め方:対象サイトの DOM を分析し、
:has()ルールを作成・テスト
2. Declarative Net Request でリクエストヘッダー管理
- やること:User-Agent 統一、トラッキングヘッダー削除など HTTP ヘッダーを宣言的に変更
- 価値:全リクエストを傍受して変更するより省電力で、ページ内容アクセスも不要
- 始め方:manifest v3 で
declarativeNetRequestWithHostAccessを宣言し、modifyHeaders ルールを記述、サイト別許可フローをテスト
3. 機密データを localStorage から sessionStorage へ
- やること:認証トークンや一時復号キーを localStorage から sessionStorage へ移行
- 価値:ディスクに書き込まず Safari 終了時に消去、漏洩リスク低減
- 始め方:機密データの
chrome.storage.localをchrome.storage.sessionに置き換え
4. Private Browsing と Profiles に適応
- やること:Private Browsing と複数 Profile で拡張挙動をテストし、データ分離を確認
- 価値:仕事用・個人用 Profile を同時利用するユーザーに対応
- 始め方:Safari 設定で複数 Profile を作成し各 Profile で独立テスト。
SFExtensionProfileKeyの処理を確認
5. プログラム的スクリプト登録で動的機能
- やること:ユーザー設定や現在ページに応じてコンテンツスクリプトを動的登録・解除
- 価値:全ページへの注入を避け、必要時のみ読み込んでオーバーヘッド削減
- 始め方:オプション画面に機能スイッチを用意し、ON で
registerContentScripts、OFF でunregisterContentScripts
関連セッション
- What’s new in Web Inspector — Safari Web Inspector の新しいデバッグ機能
- Rediscover Safari developer features — Safari 開発者機能の総合アップデート
- What’s new in CSS — コンテナクエリやカスケードレイヤーなど CSS の新機能
- Meet Safari for spatial computing — xrOS 上の Safari 体験
- What’s new in WebKit — WebKit エンジンの新機能と改善
コメント
GitHub Issues · utterances