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

Dave Tapuska
Dave Tapuska

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

Chrome では、ナビゲーションの高速化に取り組んできました。バックフォワード キャッシュ(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 がチェックされ、発生したイベントが最も外側のフレームのものかどうかが判断されていました。タブに複数のページがある場合、外側のフレームが複数存在するため、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 グループでお問い合わせください。