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

François Beaufort
François Beaufort

対応ブラウザ

  • Chrome: 109。 <ph type="x-smartling-placeholder">
  • Edge: 109。 <ph type="x-smartling-placeholder">
  • Firefox: サポートされていません。 <ph type="x-smartling-placeholder">
  • Safari: サポートされていません。 <ph type="x-smartling-placeholder">

ソース

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() を呼び出しても何も起こらない。

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

// 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() から返されたストリームの動画トラックが「live」ではない。
  • 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 に感謝します。