Chrome 拡張機能: API を拡張してインスタント ナビゲーションをサポート

Dave Tapuska
Dave Tapuska

要約: Extensions API が更新され、バックフォワード キャッシュとナビゲーションの事前読み込みがサポートされるようになりました。詳しくは以下をご覧ください。

Chrome は、ナビの高速化に取り組んできました。Back/Forward Cache(Chrome 96 でデスクトップにリリース)や投機ルール(Chrome 103 でリリース)などのインスタント ナビゲーション テクノロジーにより、前後両方のエクスペリエンスが向上します。この記事では、これらの新しいワークフローに適応するためにブラウザ拡張機能 API に加えた更新について説明します。

ページの種類について

バックフォワード キャッシュとプリレンダリングが導入される前は、個々のタブにはアクティブなページが 1 つしかありませんでした。常にこのカードが表示されていました。ユーザーが前のページに戻ると、アクティブなページは破棄され(ページ B)、履歴内の前のページが完全に再構築されます(ページ A)。タブにはアクティブ / 可視状態しかなかったため、拡張機能はライフサイクルのどの部分にあるページであるかを気にする必要はありませんでした。

アクティブなページの強制排除
アクティブなページの強制排除。

バックフォワード キャッシュとプリレンダリングを使用すると、タブとページの間に 1 対 1 の関係がなくなります。各タブは実際に複数のページを保存し、ページは破棄されて再構築されるのではなく、状態間を遷移します。

たとえば、あるページは、事前レンダリングされた(非表示の)ページで開始され、ユーザーがリンクをクリックするとアクティブな(表示可能な)ページに遷移し、ユーザーが別のページに移動したときにバックフォワード キャッシュ(非表示)に保存され、ページが破棄されることはありません。この記事の後半では、ページの状態を拡張機能が把握できるように公開される新しいプロパティについて説明します。

ページの種類
ページの種類。

タブには、事前レンダリングされた一連のページ(1 つだけではない)、1 つのアクティブ(表示されている)ページ、バック/フォワード キャッシュに保存された一連のページを含めることができます。

拡張機能デベロッパー向けの変更点

FrameId == 0

Chromium では、最上位/メインフレームを最外側のフレームと呼びます。

最外側のフレームの frameId が 0 であると想定している拡張機能の作成者は、問題が発生する可能性があります(以前のベスト プラクティス)。タブに複数の外側のフレーム(事前レンダリングされたページとキャッシュに保存されたページ)を設定できるようになったため、タブに外側のフレームが 1 つしかないという前提は正しくありません。frameId == 0 は引き続きアクティブなページの最も外側のフレームを表しますが、同じタブ内の他のページの最も外側のフレームはゼロ以外になります。この問題を解決するために、新しいフィールド frameType が追加されました。この投稿の「フレームが最も外側のフレームかどうかを確認するにはどうすればよいですか?」のセクションをご覧ください。

フレームとドキュメントのライフサイクル

拡張機能で問題となるもう 1 つのコンセプトは、フレームのライフサイクルです。フレームは、commit された URL に関連付けられたドキュメントをホストします。ドキュメントは(ナビゲーションなどによって)変更される可能性がありますが、frameId は変更されません。そのため、特定のドキュメントで発生した事象を frameId のみで関連付けることは困難です。ドキュメントごとに一意の識別子である documentId のコンセプトが導入されます。フレームを移動して新しいドキュメントを開くと、識別子が変更されます。このフィールドは、同じままであるため、ページのライフサイクル状態(事前レンダリング/アクティブ/キャッシュ)が変更されるタイミングを判断するのに役立ちます。

ウェブ ナビゲーション イベント

chrome.webNavigation Namespace のイベントは、ライフサイクルに応じて同じページで複数回発生することがあります。ページが移行している期間を確認する方法ページが移行したタイミングを判断するにはどうすればよいですか?のセクションをご覧ください。

ページのライフサイクルを確認するにはどうすればよいですか?

DocumentLifecycle 型が、以前 frameId が使用可能だった複数の拡張機能 API に追加されました。イベントに DocumentLifecycle タイプ(onCommitted など)が存在する場合、その値はイベントが生成された状態です。WebNavigationgetFrame() メソッドと getAllFrames() メソッドから情報をクエリすることはいつでも可能ですが、イベントの値を使用することをおすすめします。どちらの方法を使用する場合でも、イベントが生成されたときと、両方のメソッドで promise が返されたときにフレームの状態が変化する可能性があることに注意してください。

DocumentLifecycle の値は次のとおりです。

  • "prerender」: 現在はユーザーに表示されていませんが、ユーザーに表示される可能性があります。
  • "active": 現在ユーザーに表示されている。
  • "cached": バックフォワード キャッシュに保存されます。
  • "pending_deletion": 書類は破棄中です。

フレームが最も外側のフレームかどうかを判断するにはどうすればよいですか?

以前は、拡張機能で frameId == 0 がチェックされ、発生したイベントが最外側のフレームのものかどうかが判断されていました。1 つのタブに複数のページがある場合、最も外側のフレームが複数存在するため、frameId の定義に問題があります。バック / フォワード キャッシュに保存されたフレームに関するイベントは受信されません。ただし、事前レンダリングされたフレームの場合、最も外側のフレームの frameId はゼロ以外になります。したがって、frameId == 0 を最も外側のフレームかどうかを判断するシグナルとして使用するのは正しくありません。

これを支援するため、FrameType という新しい型を導入しました。これにより、フレームが本当に最外側のフレームであるかどうかを簡単に判断できるようになりました。FrameType の値は次のとおりです。

  • "outermost_frame": 通常、最上位のフレームと呼ばれます。これらの値は複数あります。たとえば、事前レンダリングしてキャッシュに保存したページには、それぞれ最上位のフレームと呼ばれる最外側のフレームがあります。
  • "fenced_frame": 将来の使用のために予約されています。
  • "sub_frame": 通常は iframe です。

DocumentLifecycleFrameType を組み合わせて、フレームがアクティブな最外側のフレームかどうかを判断できます。次に例を示します。 tab.documentLifecycle === “active” && frameType === “outermost_frame”

フレームの使用時間の問題を解決するにはどうすればよいですか?

前述のとおり、フレームはドキュメントをホストし、フレームは新しいドキュメントに移動する可能性がありますが、frameId は変更されません。このため、frameId のみを含むイベントを受信すると問題が発生します。フレームのURL を検索すると、イベントが発生したときとは異なる場合があります。これは使用時間に関する問題と呼ばれます。

この問題に対処するため、documentId(および parentDocumentId)を導入しました。webNavigation.getFrame() メソッドでは、documentId が指定されている場合、frameId が省略可能になりました。documentId は、フレームが移動されるたびに変更されます。

ページ遷移が発生したタイミングを特定するにはどうすればよいですか?

ページがステータス間で遷移するタイミングを判断するための明示的なシグナルがあります。

WebNavigation イベントを見てみましょう。

ページを初めてナビゲートすると、以下の順序で 4 つのイベントが表示されます。これらの 4 つのイベントは、DocumentLifecycle の状態が "prerender" または "active" のときに発生する可能性があります。

onBeforeNavigate
onCommitted
onDOMContentLoaded
onCompleted

以下の図は、事前レンダリングされたページがアクティブなページになると、documentId"xyz" に変わる様子を示しています。

事前レンダリングされたページがアクティブなページになると、documentId が変更されます
事前レンダリングされたページがアクティブなページになると、documentId が変更されます。

ページがバック/フォワード キャッシュまたは事前レンダリングからアクティブな状態に移行すると、さらに 3 つのイベントが発生します(ただし、DocumentLifecyle"active" です)。

onBeforeNavigate
onCommitted
onCompleted

documentId は元のイベントと同じままです。これは、documentId == xyz が有効になっている場合の例です。ページはすでに読み込まれているため、onDOMContentLoaded イベントを除き、同じナビゲーション イベントがトリガーされます。

ご意見やご質問がある場合は、chromium-extensions グループにお気軽にお尋ねください。