ドキュメント ピクチャインピクチャ仕様

草案コミュニティグループレポート,

このバージョン:
https://wicg.github.io/document-picture-in-picture/
課題追跡:
GitHub
仕様内インライン
編集者:
(Google Inc.)

概要

この仕様は、Web開発者が常に最前面に表示されるウィンドウ内にHTMLDocumentを配置できるようにします。

この文書のステータス

この仕様は Web Platform Incubator Community Group により公開されています。 W3C標準ではなく、W3C標準トラックにもありません。 W3Cコミュニティコントリビューターライセンス契約(CLA)のもとで限定的なオプトアウトが可能であり、他にも条件がありますのでご注意ください。 W3Cコミュニティおよびビジネスグループについて詳細をご確認ください。

1. はじめに

このセクションは規範的ではありません。

現在、HTMLVideoElement を ピクチャインピクチャウィンドウ(requestPictureInPicture())に表示するためのWeb APIが存在します。このため、ウェブサイトはカスタムのピクチャインピクチャ体験(PiP)を提供する能力が制限されています。 この機能を拡張し、ウェブサイトが常に最前面に表示されるウィンドウ上で完全なDocument を提供できるようにしたいと考えています。

この新しいウィンドウは、既存の open() メソッドを使って開かれる、同一オリジンの白紙ウィンドウに似ていますが、いくつかの違いがあります:

2. 依存関係

この仕様に記載されたIDL断片は、Web IDL仕様で定められた通り、適合するIDL断片として解釈される必要があります。[WEBIDL]

3. セキュリティに関する考慮事項

3.1. セキュアコンテキスト

このAPIは [SECURE-CONTEXTS] のみで利用可能です。

3.2. なりすまし

ユーザーエージェントは、DocumentPictureInPicture ウィンドウ上に十分なUIを提供し、悪意のあるウェブサイトが他のウィンドウの上に表示される能力を乱用して他サイトやシステムUIのなりすましを防止することが求められます。

3.2.1. 位置指定

ユーザーエージェントは、ウェブサイトがウィンドウの位置を設定できないようにする必要があります。これにより、ウェブサイトが意図的にウィンドウを他ページのUIの一部と思わせる場所に配置することを防ぎます。特に、moveTo()moveBy() APIは、ドキュメントピクチャインピクチャウィンドウでは無効化されなければなりません。

3.2.2. オリジンの可視性

ユーザーエージェントは、DocumentPictureInPicture ウィンドウをどのオリジンが制御しているかを常にユーザーに明確に伝える必要があります。これにより、ユーザーはコンテンツの出所を認識できます。例えば、ユーザーエージェントはウィンドウのタイトルバーにウェブサイトのオリジンを表示することが考えられます。

3.2.3. 最大サイズ

ユーザーエージェントは、常に最前面に表示されるウィンドウでウェブサイトが画面全体を覆いユーザーをピクチャインピクチャウィンドウに閉じ込めることを防ぐため、ドキュメントピクチャインピクチャウィンドウの最大サイズを制限すべきです。これによりユーザーのデスクトップのなりすましも防止できます。

3.2.4. 全画面表示

ユーザーエージェントは、文書のピクチャーインピクチャーウィンドウが全画面表示に入ることを、最大サイズの制限と同じ理由により防がなければならない。特に、これは requestFullscreen() API を、ドキュメントのピクチャーインピクチャーウィンドウ内のすべての要素で無効化しなければならないことを意味する。

3.3. IFrame

このAPIは 最上位トラバーサブル でのみ利用できます。ただし、DocumentPictureInPicture Window には HTMLIFrameElement を含めることができ、 クロスオリジンHTMLIFrameElement も利用可能です。

4. プライバシーに関する考慮事項

4.1. フィンガープリント

PiPウィンドウを閉じてから後で再度開いた場合、前回のPiPウィンドウのサイズや場所を再利用することでユーザー体験を向上できることがあります。しかし、ユーザーエージェントは異なるオリジン間でサイズや位置を再利用しないことが推奨されます。これは悪意のあるウェブサイトによるフィンガープリントの手段となる可能性があるためです。

5. API

[Exposed=Window]
partial interface Window {
  [SameObject, SecureContext] readonly attribute DocumentPictureInPicture
    documentPictureInPicture;
};

[Exposed=Window, SecureContext]
interface DocumentPictureInPicture : EventTarget {
  [NewObject] Promise<Window> requestWindow(
    optional DocumentPictureInPictureOptions options = {});
  readonly attribute Window window;
  attribute EventHandler onenter;
};

dictionary DocumentPictureInPictureOptions {
  [EnforceRange] unsigned long long width = 0;
  [EnforceRange] unsigned long long height = 0;
  boolean disallowReturnToOpener = false;
  boolean preferInitialWindowPlacement = false;
};

[Exposed=Window, SecureContext]
interface DocumentPictureInPictureEvent : Event {
  constructor(DOMString type, DocumentPictureInPictureEventInit eventInitDict);
  [SameObject] readonly attribute Window window;
};

dictionary DocumentPictureInPictureEventInit : EventInit {
  required Window window;
};
DocumentPictureInPicture オブジェクトは、ウェブサイトが新しい常に最前面の Window を作成・開くこと、そしてその Window の開閉に関連するイベントを監視することを可能にします。

Window オブジェクトは、関連付けられた documentPictureInPicture API を持ちます。これは DocumentPictureInPicture の新しいインスタンスであり、Window の作成時に同時に生成されます。

documentPictureInPicture ゲッタの手順は:
  1. thisdocumentPictureInPicture API を返す。

DocumentPictureInPicture オブジェクトは、関連付けられた last-opened window(最後に開かれたウィンドウ) を持ちます。これは Window オブジェクトであり、初期状態では null です。 これは requestWindow() メソッドの手順の一部として設定されます。

window ゲッタの手順は:
  1. winthislast-opened window とする。

  2. winnull でなく、かつ winclosed 属性が false であれば、win を返す。

  3. null を返す。

requestWindow(options) メソッドの手順は:
  1. Document Picture-in-Picture サポートfalse の場合、 "NotSupportedError" DOMException をスローする。

  2. thisrelevant global objectnavigableトップレベルの traversable(走査可能)でなければ、 "NotAllowedError" DOMException をスローする。

  3. thisrelevant global objectnavigableIs Document Picture-in-Picture ブール値が true の場合、 "NotAllowedError" DOMException をスローする。

  4. thisrelevant global objecttransient activation(一時的なアクティベーション) を持っていなければ、 "NotAllowedError" DOMException をスローする。

  5. もし options["width"] が存在し、値が0より大きいが、 options["height"] が存在しない、または0の場合は、 RangeError をスローする。

  6. もし options["height"] が存在し、かつ値が0より大きいが、 options["width"] が存在しないか0の場合は、 RangeError をスローする。

  7. ユーザーアクティベーションを消費するthisrelevant global objectで)。

  8. winthislast-opened window とする。winnull でなく、かつ winclosed 属性が false なら、 close winnavigable を。

  9. ユーザーエージェントは 既存のピクチャーインピクチャーウィンドウを閉じる こともできる(任意)。

  10. pip traversable新しいトップレベル traversable を作成 の結果をセットする。引数として thisrelevant global objectnavigableアクティブな browsing context と "_blank" を使う。

生成される DocumentURL は `about:blank` ですが、 文書のベース URLrequestWindow() を呼び出した発信者のものをフォールバックとして使用します。 一部ブラウザは通常の `about:blank` ポップアップにはこのフォールバック動作を実装していません。 議論は whatwg/html#421 を参照。 実装者は、ドキュメントピクチャーインピクチャーウィンドウについては指定どおり継承させ、 さらなる互換性問題を避けるよう推奨されます。

  1. pip traversableアクティブなドキュメントmodethisrelevant global object関連付けられた Documentmode にセットする。

  2. pip traversableIs Document Picture-in-Picture ブール値を true にセットする。

  3. もし options["width"] が存在し、値が0より大きいなら:

    1. 必要に応じて、options["width"] が大きすぎる/小さすぎる場合はユーザーフレンドリーなウィンドウサイズに収まるように丸めたり無視してもよい。

    2. 必要に応じて、pip traversableアクティブな browsing context のウィンドウのビューポート左右端間距離を options["width"] ピクセルにしてもよい。

  4. もし options["height"] が存在し、値が0より大きい場合:

    1. 必要に応じて、options["height"] が大きすぎる/小さすぎる場合はユーザーフレンドリーなウィンドウサイズに収まるように丸めたり無視してもよい。

    2. 必要に応じて、pip traversableアクティブな browsing context のウィンドウのビューポート上下端間距離を options["height"] ピクセルにしてもよい。

もし options["preferInitialWindowPlacement"] が存在し true なら、ユーザーエージェントはこのヒントに従い、以前閉じた pip traversable ウィンドウの位置やサイズより、手順13・14類似の動作を優先してもよい。

  1. もし options["disallowReturnToOpener"] が存在し true なら、ユーザーエージェントは ピクチャーインピクチャーウィンドウ上で、 ユーザーがオープナーウィンドウへ戻る操作UIを表示しないようにすべきである。

ビデオ・ドキュメントピクチャーインピクチャーの両方で、ユーザーエージェントはよく 「元ページに戻る」ボタンやピクチャーインピクチャーウィンドウを閉じる ボタンを表示します。この操作は大抵妥当ですが(特にビデオPiPでは元ドキュメントに ビデオを戻します)、ドキュメントPiPウィンドウではそうとは限りません。 disallowReturnToOpener は、ウェブサイト側からユーザーエージェントへの 「このドキュメントPiP体験ではそのアクションは妥当か否か」のヒントです。

  1. pip traversableアクティブな browsing context のウィンドウを 他のウィンドウの上にフロートさせるよう構成する。

  2. thislast-opened windowpip traversableアクティブなウィンドウ に設定する。

  3. グローバルタスクをキューイングする。 DOM 操作タスクソース上で、 thisrelevant global objectイベントを発火する。 イベント名は enter であり、DocumentPictureInPictureEvent を使い、 this で、 その window 属性に pip traversableアクティブなウィンドウ をセットして。

  4. Promise を resolve しpip traversableアクティブなウィンドウ を返す。

ウィンドウのサイズはウェブサイト側で設定できるが、初期位置はユーザーエージェントの裁量に委ねられる。

enter

PiP ウィンドウが開かれたとき、DocumentPictureInPicture 上で発火される。

6. 概念

6.1. ドキュメントピクチャインピクチャのサポート

各ユーザーエージェントは ドキュメントピクチャインピクチャサポート 真理値を持ち、その値は 実装依存(ユーザーの設定によって変化する場合がある)です。

6.2. DocumentPictureInPicture ウィンドウ

トップレベル traversable は、「ドキュメントピクチャーインピクチャーかどうか」 というブール値を持つ。この値はデフォルトでは false であるが、requestWindow() メソッドの手順中で true に設定されることがある。

ユーザーエージェントは、最小化されたウィンドウや他の理由でユーザーに表示されていないウィンドウでは、レンダリングを停止したりスクリプトの実行頻度を下げたりすることが一般的である。ドキュメントピクチャーインピクチャーウィンドウが開いている場合、オープナーウィンドウが実行するスクリプトが、ドキュメントピクチャーインピクチャーウィンドウ内のコンテンツへ影響を及ぼすことがある。

実装者はこのようなオープナーウィンドウをスロットリングする影響を検討することが推奨される。また、開発者はピクチャーインピクチャーウィンドウ自身の中でアプリケーションロジックを実行することが推奨される。

6.3. ドキュメントピクチャインピクチャウィンドウの閉じ方

これに十分なコンセンサスが得られたら、 definitely close に統合すること。

definitely close の 手順2 "If the result of checking if unloading is user-canceled for toUnload is not "continue", then return." を次のように変更する:

  1. もし traversableIs Document Picture-in-Picture ブール値が true なら、このステップをスキップする。そうでなければ アンロードがユーザーによりキャンセルされたかの確認 を toUnload で実行した結果が "continue" でなければ、return する。

6.4. 既存のPiPウィンドウを閉じる

既存のピクチャインピクチャウィンドウを閉じるには:

  1. ユーザーエージェントの 最上位トラバーサブル集合 の各 top-level traversable に対して:

    1. もし top-level traversableドキュメントピクチャインピクチャかどうか真理値が true なら、close top-level traversableを実行。

    2. もし top-level traversableactive documentpictureInPictureElementnullでなければ、ピクチャインピクチャ終了アルゴリズムtop-level traversableactive documentに対して実行。

    3. top-level traversableactive document子孫ナビゲーブルnavigable に対して:

      1. もし navigableactive documentpictureInPictureElementnullでなければ、ピクチャインピクチャ終了アルゴリズムnavigableactive documentに対して実行。

6.5. PiPウィンドウは1つだけ

どの最上位トラバーサブルも同時に開いているドキュメントピクチャインピクチャウィンドウは最大1つでなければならない。最上位トラバーサブル でそのactive windowdocumentPictureInPicture APIlast-opened windownullでなければ、 さらにドキュメントピクチャインピクチャウィンドウを開こうとしたとき、ユーザーエージェントは既存のlast-opened windowrequestWindow()メソッドの手順に従い閉じなければならない。

ただし、全最上位トラバーサブルでピクチャインピクチャモードのウィンドウがひとつだけ許可されるかどうかは、実装とプラットフォームによる。 そのため、すでに最上位トラバーサブルドキュメントピクチャインピクチャかどうか真理値がtrueだったり active documentpictureInPictureElementnullでない場合にピクチャインピクチャ要求が来たらどうなるかは実装依存である。ユーザーエージェントは既存のピクチャインピクチャウィンドウを閉じる動作や 複数のピクチャインピクチャウィンドウを作成する動作もあり得る。

6.6. 元のドキュメントやPiPドキュメントが破棄された時にPiPウィンドウを閉じる

関連付けられたドキュメントピクチャインピクチャウィンドウを閉じる、与えられた Document documentに対して:

  1. navigabledocumentnode navigableとする。

  2. もしnavigable最上位トラバーサブルでなければこれらの手順を中止。

  3. もしnavigableドキュメントピクチャインピクチャかどうか真理値がtrueなら close navigableを実行してこの手順を中止。

  4. winnavigableactive windowdocumentPictureInPicture APIlast-opened windowとする。

  5. もしwinnullでなく、winclosed属性がfalseなら、close winnavigableを実行。

destroy に十分な合意が得られたら統合すること。

destroy の最後に「ステップ10」を追加:

  1. 関連付けられたドキュメントピクチャインピクチャウィンドウを閉じる、与えられたdocumentに対して。

開いているドキュメントピクチャインピクチャウィンドウがあるページを閉じた時、そのPiPウィンドウも閉じることを保証するためです。

6.7. 元のドキュメントやPiPドキュメントが遷移した時にPiPウィンドウを閉じる

これに十分な合意が得られたら navigate に統合すること。

navigate の手順23.3 「navigableのアクティブウィンドウを指定してnavigation and traversal task sourceでグローバルタスクをキューイングし、navigableのアクティブドキュメントを指定してドキュメントおよびその子孫を中断する」 を修正し、その直後にステップ23.4を挿入する:

  1. グローバルタスクをキューイング する。 navigation and traversal task source で、navigableアクティブウィンドウ を指定して、 ドキュメントおよびその子孫の中断navigableアクティブドキュメント で行い、 関連付けられたDocument Picture-in-Pictureウィンドウを閉じるnavigableアクティブドキュメント で実行する。

  2. もし navigableトップレベルtraversable で その 「ドキュメントピクチャーインピクチャーかどうか」ブール値が true なら、これらの手順を中断する。

これにより、Document Picture-in-Picture ウィンドウが開いているページがナビゲートされた場合、そのPiPウィンドウも同時に閉じられることが保証される。また、Document Picture-in-Picture ウィンドウ内のドキュメントがナビゲートされたときにも、Document Picture-in-Picture ウィンドウが閉じられることを保証する。

6.8. PiPウィンドウのサイズ変更

ドキュメントピクチャインピクチャウィンドウをプログラムでサイズ変更することは便利ですが、常時最前面の特性のため、サイズ変更の自由度が高いと迷惑・侵襲的な使われ方をされるおそれがあります。 そのため、ウィンドウサイズ変更APIの利用にはユーザー操作を消費する形にして一部懸念を緩和します。

resizeTo() に十分な合意が得られたら統合すること。

resizeTo() のステップ3「targetがスクリプトによって生成された補助ブラウジングコンテキストでないなら return」の後ろに次の新手順を追加:

  1. もし target最上位トラバーサブルドキュメントピクチャインピクチャかどうかtrueなら:

    1. thisrelevant global objecttransient activationがなければ、"NotAllowedError" DOMExceptionをスロー。

    2. consume user activationthisrelevant global objectで実行。

resizeBy() に十分な合意が得られたら統合すること。

resizeBy() のステップ3「targetがスクリプトによって生成された補助ブラウジングコンテキストでないなら return」の後ろに次の新手順を追加:

  1. もし target最上位トラバーサブルドキュメントピクチャインピクチャかどうかtrueなら:

    1. thisrelevant global objecttransient activationがなければ、"NotAllowedError" DOMExceptionをスロー。

    2. consume user activationthisrelevant global objectで実行。

6.9. PiPウィンドウの移動

§ 3.2.1 配置 に記載されているスプーフィングを防ぐため、ドキュメントピクチャーインピクチャーウィンドウでは moveTo() および moveBy() を無効化します。

本件について十分な合意が得られたら、moveTo() および moveBy() の手順に統合してください。

moveTo() の手順3の後に、次の新しい手順を1つ追加します。 "If target is not an スクリプト(ユーザーの操作ではなく)によって作成された 補助閲覧コンテキスト(auxiliary browsing context)でない場合は、returnする。":

  1. もし targetトップレベルtraversableIs Document Picture-in-Picture ブール値が true なら、returnする。

moveBy() の手順3の後に、次の新しい手順を1つ追加します。 "If target is not an スクリプト(ユーザーの操作ではなく)によって作成された 補助閲覧コンテキスト(auxiliary browsing context)でない場合は、returnする。":

  1. もし targetトップレベルtraversableIs Document Picture-in-Picture ブール値が true なら、returnする。

6.10. オープナーウィンドウへのフォーカス

PiPウィンドウがオープナーのタブに再度フォーカスできると便利な場合が多いです。たとえば、ウィンドウが小さいフォームファクターだとユーザーの期待する体験に十分でないときです。 そこで focus() API は、PiPウィンドウがオープナーにフォーカスする際にシステムレベルのフォーカスを取得できるよう修正します。

本件について十分な合意が得られたら、focus() に統合してください。

focus() の手順4「focusing stepscurrent で実行する。」の後に新しい手順を追加します:

  1. もし currentトップレベルtraversable なら:

    1. pipWindowcurrentアクティブウィンドウdocumentPictureInPicture APIlast-opened window とする。

    2. もし pipWindownull でなく、かつ pipWindowrelevant global object一時的なアクティベーション を持っている場合:

      1. ユーザーアクティベーションを消費するpipWindowrelevant global object に対して実行する。

      2. currentシステムフォーカス を与える。

オープナーにシステムフォーカスを与えても、必ずしもドキュメントピクチャーインピクチャーウィンドウを閉じる必要はありません。ウェブサイトがフォーカス後にPiPウィンドウを閉じたい場合は、 PiPウィンドウ自身で close() を使うことができます。

6.11. CSS display-mode

CSS の display mode メディア機能 picture-in-picture により、ウェブアプリがピクチャーインピクチャーモードで表示されているときだけ適用される 特定のCSSルールを開発者が記述できます。

6.12. ユーザーアクティベーションの伝播

ドキュメントピクチャーインピクチャーウィンドウの特性上、 ウィンドウ内のボタンのイベントハンドラが 実際にはオープナーのコンテキストで動作することがよくあります。 そのため、ドキュメントPiPウィンドウに activation consuming APIs を呼び出すのが開発者にとって使いにくくなることがあります。 なぜなら時にはドキュメントPiPウィンドウには 一時的なアクティベーション があり、 オープナーには無い場合があるためです。

この問題を解消するため、 activation notification の手順を、ドキュメントPiPウィンドウでユーザーアクティベーションが発生した時にオープナー側も アクティブにし、逆も同様にオープナーでアクティベーションされた時は PiPウィンドウ内の同一オリジンのフレームもアクティベートされるように拡張します(同一オリジンの子孫フレームの処理と同様)。

本件について十分な合意が得られたら、 activation notification の手順に統合してください。

activation notification の手順4「Extend windowsdocumentdescendant navigablesactive window で拡張する。 ただし、navigables のうち その active documentorigin同一オリジン であるものだけを含める」の後に新しい3手順を追加する:

  1. もし documentnode navigableトップレベルtraversableIs Document Picture-in-Picture ブール値が true なら、 windowsdocumentnode navigableトップレベルtraversableactive browsing contextopener browsing contextactive window で拡張する。

  2. document picture-in-picture windowdocumentnode navigableトップレベルtraversableactive windowdocumentPictureInPicture APIlast-opened window とする。

  3. もし document picture-in-picture windownull でなければ、 windowsdocument picture-in-picture windowassociated documentdescendant navigablesactive window で拡張する。 ただし、navigables のうち その active documentorigin同一オリジン であるものだけを含める。

さらに、このアクティベーションが正しく消費されて、(オープナーとPiPウィンドウで)二重消費されないことを保証する必要があります。 そのため consume user activation にも手順を追加し、 PiPウィンドウのアクティベーション消費時にはオープナーからも消費し、逆も同様にオープナー消費時には関連するPiPウィンドウのアクティベーションも消費するようにします。

本件について十分な合意が得られたら consume user activation の手順に統合してください。

consume user activation の手順3 「Let navigables be the inclusive descendant navigables of top’s active document.」の後に3手順を追加:

  1. もし topIs Document Picture-in-Picture ブール値が true なら、 navigablestopactive browsing contextopener browsing contextactive document の inclusive descendant navigables で拡張する。

  2. document picture-in-picture windowtopactive windowdocumentPictureInPicture APIlast-opened window とする。

  3. もし document picture-in-picture windownull でなければ navigablesdocument picture-in-picture windowassociated document の inclusive descendant navigables で拡張する。

6.13. PiPウィンドウの全画面表示

§ 3.2.4 全画面 で述べた通り、 ドキュメントピクチャーインピクチャーウィンドウは全画面表示できません。 この制限を強制するため fullscreen is supported を更新します。

十分な合意が得られたら、 fullscreen is supported に統合してください。

fullscreen is supported の "if there is no previously-established user preference, security risk, or platform limitation." を以下に置き換えます:

Fullscreen is supportedWindow window について、以下全てが true の場合のみ成立する:

fullscreen is supported を呼ぶ際は Window を必ず渡すように修正が必要です。

7.

このセクションは規範的ではありません

7.1. 動画プレイヤーをPiPへ抽出する

7.1.1. HTML

<body>
  <div id="player-container">
    <div id="player">
      <video id="video" src="foo.webm"></video>
      <!-- プレーヤーの追加要素 -->
    </div>
  </div>
  <input type="button" onclick="enterPiP();" value="PiPに入る" />
</body>

7.1.2. JavaScript

// ピクチャインピクチャウィンドウのハンドル。
let pipWindow = null;

function enterPiP() {
  const player = document.querySelector('#player');

  // ウィンドウサイズを適切に設定するため、width/heightを指定。
  const pipOptions = {
    width: player.clientWidth,
    height: player.clientHeight,
  };

  documentPictureInPicture.requestWindow(pipOptions).then((pipWin) => {
    pipWindow = pipWin;

    // プレイヤーがPiPへ移行したことを示すコンテナのスタイル変更。
    playerContainer.classList.add('pip-mode');

    // プレイヤーをPiPウィンドウへ追加。
    pipWindow.document.body.append(player);

    // PiP終了イベント監視してプレイヤーを戻す。
    pipWindow.addEventListener('pagehide', onLeavePiP.bind(pipWindow), { once: true });
  });
}

// PiPウィンドウが閉じた時呼ばれる。
function onLeavePiP() {
  if (this !== pipWindow) {
    return;
  }

  // コンテナのPiPスタイルを除去。
  const playerContainer = document.querySelector('#player-container');
  playerContainer.classList.remove('pip-mode');

  // プレイヤーをメインウィンドウへ戻す。
  const player = pipWindow.document.querySelector('#player');
  playerContainer.append(player);

  pipWindow = null;
}

7.2. PiPウィンドウで要素にアクセスする

const video = pipWindow.document.querySelector('#video');
video.loop = true;

7.3. PiPウィンドウでイベントをリッスンする

より良いピクチャインピクチャ体験のため、ウェブサイトではユーザー入力イベント(クリックなど)に反応するカスタムボタンやコントロールを実装することがよくあります。

const pipDocument = pipWindow.document;
const video = pipDocument.querySelector('#video');
const muteButton = pipDocument.document.createElement('button');
muteButton.textContent = 'ミュート切替';
muteButton.addEventListener('click', () => {
  video.muted = !video.muted;
});
pipDocument.body.append(muteButton);

7.4. PiPを終了する

ウェブサイトは、ユーザーがウィンドウの閉じるボタンを明示的にクリックしなくても、DocumentPictureInPictureWindow を閉じたい場合があります。これは、そのWindow オブジェクトでclose()メソッドを使うことで実現できます。

// これでPiPウィンドウを閉じ、onLeavePiP()が実行されます。
// リスナー付き。
pipWindow.close();

7.5. PiPウィンドウが閉じる時に要素を取得する

PiPウィンドウを閉じる理由が何であれ(ウェブサイトが閉じる・ユーザーが閉じる)、要素をPiPウィンドウから元に戻したくなる場合がよくあります。これはpagehide イベントのハンドラで実装可能です。上記動画プレイヤー例onLeavePiP()でも示されています:

// PiPウィンドウが閉じた時呼ばれる。
function onLeavePiP() {
  if (this !== pipWindow) {
    return;
  }

  // コンテナのPiPスタイルを除去。
  const playerContainer = document.querySelector('#player-container');
  playerContainer.classList.remove('pip-mode');

  // プレイヤーをメインウィンドウへ戻す。
  const player = pipWindow.document.querySelector('#player');
  playerContainer.append(player);

  pipWindow = null;
}

7.6. PiPウィンドウをプログラムでサイズ変更する

ドキュメントピクチャインピクチャウィンドウはresizeTo()resizeBy() APIをサポートしていますが、PiPウィンドウ上でのユーザー操作が必要です:

const expandButton = pipWindow.document.createElement('button');
expandButton.textContent = 'PiPウィンドウを拡張';
expandButton.addEventListener('click', () => {
  // PiPウィンドウの幅を20px、高さを30px拡張。
  pipWindow.resizeBy(20, 30);
});
pipWindow.document.body.append(expandButton);

7.7. オープナータブに戻る

focus() APIを使えば、ピクチャインピクチャウィンドウからオープナータブにフォーカスを移動でき(要ユーザー操作):

const returnToTabButton = pipWindow.document.createElement('button');
returnToTabButton.textContent = 'オープナータブに戻る';
returnToTabButton.addEventListener('click', () => {
  window.focus();
});
pipWindow.document.body.append(returnToTabButton);

7.8. CSSピクチャインピクチャディスプレイモードの使用例

次の例は、PiPウィンドウ内の body 要素のマージンを除去し、タイトルのフォントサイズを小さくしてコンテンツをウィンドウにフィットさせる方法を示します:

@media all and (display-mode: picture-in-picture) {
  body {
    margin: 0;
  }
  h1 {
    font-size: 0.8em;
  }
}

7.9. 戻るボタンの非表示

多くのユーザーエージェントは、動画やドキュメントピクチャインピクチャウィンドウでオープナーに戻ってウィンドウを閉じるボタンを表示しますが、これは一部のウェブサイトのドキュメントピクチャインピクチャ体験では適していない場合もあります。disallowReturnToOpener オプションでこのボタンを非表示にできます。

await documentPictureInPicture.requestWindow({
  disallowReturnToOpener: true
});

7.10. 初期ウィンドウ配置の優先

ドキュメントピクチャインピクチャウィンドウは開いている間にユーザーがサイズ変更や再配置する場合があります。閉じてから再度開いた時、ユーザーエージェントは前回の位置・サイズをヒントとして使い、ウィンドウをデフォルト位置以外に配置する場合もあります。

前回のウィンドウ位置・サイズを使って欲しくない場合、preferInitialWindowPlacement をtrueに指定してヒントを出すことができます。例えば、新規の活動用に新しいPiPウィンドウを開く場合などです。これによりユーザーエージェントはデフォルト位置やサイズ、あるいはサイトが指定したサイズヒント通りに表示する場合があります。

await documentPictureInPicture.requestWindow({
  preferInitialWindowPlacement: true
});

8. 謝辞

Frank Liberato、Mark Foltz、Klaus Weidner、François Beaufort、Charlie Reis、Joe DeBlasio、Domenic Denicola、Yiren Wang各氏の意見や貢献、また本ドキュメントと議論に寄せられた知見に感謝します。

適合性

ドキュメントの規則

適合要件は記述的な論述とRFC 2119の用語の組み合わせで表現されます。 規範部分で使われている “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL” などの主要語は、 RFC 2119に定められる通り解釈されます。 なお、可読性向上のためこれらの語は全て大文字とはしません。

非規範的と明記されたセクション、例、注以外はすべて規範的文言です。 [RFC2119]

この仕様書の例は「for example」などで導入されるほか、class="example" により規範テキストから明確に区別されます。

これは参考例です。

参考注記は「Note」から始まり、class="note" により規範テキストと分離されます。

これは参考注記です。

索引

この仕様で定義された用語

参照で定義された用語

参考文献

規格文献

[CSSOM-VIEW-1]
Simon Fraser; Emilio Cobos Álvarez. CSSOM View Module(CSSOM ビューモジュール).URL: https://drafts.csswg.org/cssom-view/
[DOM]
Anne van Kesteren. DOM Standard(DOM標準).Living Standard. URL: https://dom.spec.whatwg.org/
[FULLSCREEN]
Philip Jägenstedt. Fullscreen API Standard(全画面API標準). Living Standard.URL: https://fullscreen.spec.whatwg.org/
[HTML]
Anne van Kesteren; 他. HTML Standard(HTML標準). Living Standard.URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard(インフラ標準).Living Standard.URL: https://infra.spec.whatwg.org/
[MEDIAQUERIES-5]
Dean Jackson; 他. Media Queries Level 5(メディアクエリ レベル5).URL: https://drafts.csswg.org/mediaqueries-5/
[RFC2119]
S. Bradner. RFCsで要求レベルを示すためのキーワード. 1997年3月.Best Current Practice.URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard(Web IDL標準).Living Standard.URL: https://webidl.spec.whatwg.org/

参考文献

[SECURE-CONTEXTS]
Mike West. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/

IDL索引

[Exposed=Window]
partial interface Window {
  [SameObject, SecureContext] readonly attribute DocumentPictureInPicture
    documentPictureInPicture;
};

[Exposed=Window, SecureContext]
interface DocumentPictureInPicture : EventTarget {
  [NewObject] Promise<Window> requestWindow(
    optional DocumentPictureInPictureOptions options = {});
  readonly attribute Window window;
  attribute EventHandler onenter;
};

dictionary DocumentPictureInPictureOptions {
  [EnforceRange] unsigned long long width = 0;
  [EnforceRange] unsigned long long height = 0;
  boolean disallowReturnToOpener = false;
  boolean preferInitialWindowPlacement = false;
};

[Exposed=Window, SecureContext]
interface DocumentPictureInPictureEvent : Event {
  constructor(DOMString type, DocumentPictureInPictureEventInit eventInitDict);
  [SameObject] readonly attribute Window window;
};

dictionary DocumentPictureInPictureEventInit : EventInit {
  required Window window;
};

課題索引

十分な合意が得られたら、 definitely close に統合してください。
十分な合意が得られたら、 destroy に統合してください。
十分な合意が得られたら、 navigate に統合してください。
十分な合意が得られたら、resizeTo() に統合してください。
十分な合意が得られたら、resizeBy() に統合してください。
十分な合意が得られたら、moveTo() および moveBy() の手順に統合してください。
十分な合意が得られたら、focus() に統合してください。
十分な合意が得られたら、 activation notification の手順に統合してください。
十分な合意が得られたら、consume user activation の手順に統合してください。
十分な合意が得られたら、fullscreen is supported に 統合してください。