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

Dave Tapuska 氏
Dave Tapuska

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

Chrome では、ナビゲーションを高速化するよう努めています。バックフォワード キャッシュ(Chrome 96 でパソコン向けに提供)や推測ルール(Chrome 103 で提供)などのインスタント ナビゲーション テクノロジーにより、過去にさかのぼっても、今後のエクスペリエンスも向上します。この投稿では、こうした新しいワークフローに対応するために、ブラウザ拡張機能 API に加えられた更新について説明します。

ページの種類について

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

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

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

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

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

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

拡張機能のデベロッパーにとっての変更点

フレーム ID == 0

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

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

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

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

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

chrome.webNavigation 名前空間内のイベントは、そのライフサイクルに応じて、同じページで複数回呼び出すことができます。「ページのライフサイクルを確認するにはどうすればよいですか?」「ページが遷移したタイミングを確認するには、どうすればよいですか?」をご覧ください。

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

DocumentLifecycle タイプは、これまで frameId が利用可能だった多くの拡張機能 API に追加されました。DocumentLifecycle 型がイベント(onCommitted など)に存在する場合、その値はイベントが生成された状態です。WebNavigation getFrame() メソッドと getAllFrames() メソッドからの情報はいつでもクエリできますが、イベントの値を使用することが常に推奨されます。いずれかのメソッドを使用する場合は、イベントが生成されてから、両方のメソッドによる Promise の戻り値が解決されるまでに、フレームの状態が変わる可能性があります。

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

  • "prerender" : 現在ユーザーには表示されていませんが、ユーザーに表示される準備をしています。
  • "active": 現在ユーザーに表示されます。
  • "cached": バックフォワード キャッシュに保存されます。
  • "pending_deletion": ドキュメントは破棄されています。

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

以前は、拡張機能が frameId == 0 をチェックし、発生しているイベントが最も外側のフレームに関するものかどうかを判断していました。タブ内に複数のページがあると、最も外側のフレームが複数存在するようになるため、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 グループまでお気軽にお寄せください。