条件付きフォーカスによる画面共有の改善

François Beaufort
François Beaufort

対応ブラウザ

  • Chrome: 109.
  • Edge: 109。
  • Firefox: サポートされていません。
  • Safari: サポートされていません。

ソース

Screen Capture API を使用すると、ユーザーはタブ、ウィンドウ、画面を選択してメディア ストリームとしてキャプチャできます。このストリームは、録画したり、ネットワーク経由で他のユーザーと共有したりできます。このドキュメントでは、キャプチャの開始時にキャプチャされたタブまたはウィンドウにフォーカスを当てるかどうか、またはキャプチャ元のページにフォーカスを当てるかどうかをウェブアプリが制御するメカニズムである条件付きフォーカスについて説明します。

ブラウザ サポート

条件付きフォーカスは Chrome 109 以降で利用できます。

背景

ウェブアプリがタブまたはウィンドウのキャプチャを開始すると、ブラウザはキャプチャされたサーフェスをフォアグラウンドに移動するか、キャプチャ ページにフォーカスを維持するかを決定する必要があります。答えは、getDisplayMedia() を呼び出す理由と、ユーザーが最終的に選択するサーフェスによって異なります。

架空のビデオ会議ウェブアプリについて考えてみましょう。ビデオ会議ウェブアプリは、track.getSettings().displaySurface を読み取り、必要に応じてキャプチャ ハンドルを調べることで、ユーザーが共有することを選択した内容を把握できます。以下の手順を行います。

  • キャプチャされたタブまたはウィンドウをリモートで操作できる場合は、ビデオ会議にフォーカスを合わせます。
  • そうでない場合は、キャプチャしたタブまたはウィンドウにフォーカスを合わせます。

上記の例では、スライド デッキを共有する場合、ビデオ会議ウェブアプリはフォーカスを保持し、ユーザーはリモートでスライドをめくることができます。ただし、ユーザーがテキスト エディタを共有する場合、ビデオ会議ウェブアプリはキャプチャされたタブまたはウィンドウにフォーカスをすぐに切り替えます。

Conditional Focus API の使用

CaptureController をインスタンス化して getDisplayMedia() に渡します。getDiplayMedia() から返された Promise が解決した直後に setFocusBehavior() を呼び出すことで、キャプチャされたタブまたはウィンドウにフォーカスを当てるかどうかを制御できます。これは、ユーザーがタブまたはウィンドウを共有した場合にのみ行えます。

const controller = new CaptureController();

// Prompt the user to share a tab, a window or a screen.
const stream =
    await navigator.mediaDevices.getDisplayMedia({ controller });

const [track] = stream.getVideoTracks();
const displaySurface = track.getSettings().displaySurface;
if (displaySurface == "browser") {
  // Focus the captured tab.
  controller.setFocusBehavior("focus-captured-surface");
} else if (displaySurface == "window") {
  // Do not move focus to the captured window.
  // Keep the capturing page focused.
  controller.setFocusBehavior("focus-capturing-application");
}

フォーカスするかどうかを判断する際に、キャプチャ ハンドルを考慮できます。

// Retain focus if capturing a tab dialed to example.com.
// Focus anything else.
const origin = track.getCaptureHandle().origin;
if (displaySurface == "browser" && origin == "https://example.com") {
  controller.setFocusBehavior("focus-capturing-application");
} else if (displaySurface != "monitor") {
  controller.setFocusBehavior("focus-captured-surface");
}

getDisplayMedia() を呼び出す前にフォーカスするかどうかを決めることもできます。

// Focus the captured tab or window when capture starts.
const controller = new CaptureController();
controller.setFocusBehavior("focus-captured-surface");

// Prompt the user to share their screen.
const stream =
    await navigator.mediaDevices.getDisplayMedia({ controller });

setFocusBehavior() は、Promise が解決する前に任意の数回、または Promise が解決した直後に最大 1 回呼び出すことができます。最後の呼び出しが、それ以前のすべての呼び出しをオーバーライドします。

正確には、getDisplayMedia() が返す Promise はマイクロタスクで解決されます。そのマイクロタスクの完了後に setFocusBehavior() を呼び出すと、エラーがスローされます。- キャプチャの開始から 1 秒以上経過した後に setFocusBehavior() を呼び出しても、何も処理されません。

つまり、次の 2 つのスニペットはどちらも失敗します。

// Prompt the user to share their screen.
const stream =
    await navigator.mediaDevices.getDisplayMedia({ controller });

// Too late, because it follows the completion of the task
// on which the getDisplayMedia() promise resolved.
// This will throw.
setTimeout(() => {
  controller.setFocusBehavior("focus-captured-surface");
});
// Prompt the user to share their screen.
const stream =
    await navigator.mediaDevices.getDisplayMedia({ controller });

const start = new Date();
while (new Date() - start <= 1000) {
  // Idle for ≈1s.
}

// Because too much time has elapsed, the browser will have
// already decided whether to focus.
// This fails silently.
controller.setFocusBehavior("focus-captured-surface");

setFocusBehavior() の呼び出しは、次の場合にスローされます。

  • getDisplayMedia() によって返されたストリームの動画トラックが「ライブ」ではない。
  • getDisplayMedia() から返された Promise が解決した後(ユーザーが画面(タブやウィンドウではない)を共有した場合)。

サンプル

条件付きフォーカスを試すには、Glitch でデモを実行します。ソースコードを確認してください。

特徴検出

CaptureController.setFocusBehavior() がサポートされているかどうかを確認するには、次のコマンドを使用します。

if (
  "CaptureController" in window &&
  "setFocusBehavior" in CaptureController.prototype
) {
  // CaptureController.setFocusBehavior() is supported.
}

フィードバック

Chrome チームとウェブ標準コミュニティは、条件付きフォーカスに関するご意見をお待ちしております。

デザインについて

条件付きフォーカスが期待どおりに動作しない点はありますか?または、アイデアを実装するために必要なメソッドやプロパティが不足している場合は、セキュリティ モデルについてご質問やご意見がございましたら、

  • GitHub リポジトリで仕様に関する問題を報告するか、既存の問題にコメントを追加してください。

実装に関する問題

Chrome の実装にバグが見つかりましたか?それとも、実装が仕様と異なるのでしょうか?

  • https://new.crbug.com でバグを報告します。できるだけ詳細な情報を含め、再現手順を簡単に記載してください。Glitch はコードの共有に適しています。

クリエイターを応援する

条件付きフォーカスを使用する予定はありますか?公開されている機能のサポートは、Chrome チームが機能の優先順位を決める際に役立ちます。また、他のブラウザ ベンダーに、その機能のサポートがどれほど重要であるかを示します。

@ChromiumDev にツイートして、どこでどのように使用しているかをお知らせください。

謝辞

ヒーロー画像は Elena Taranenko によるものです。

この記事を確認していただいた Rachel Andrew に感謝します。