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

Dave Tapuska
Dave Tapuska

要約: バックフォワード キャッシュをサポートするように拡張機能 API が更新され、ナビゲーションがプリロードされました。詳しくは以下をご覧ください。

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

ページの種類について

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

アクティブなページのエビクション
アクティブなページのエビクション。

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

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

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

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

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

FrameId == 0(フレーム ID == 0)

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

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

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

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

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

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

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

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 と組み合わせることで、フレームが最も外側のアクティブなフレームかどうかを判断できます。次に例を示します。 js 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 グループにお気軽にお尋ねください。