1. 소개
이 섹션은 표준이 아닙니다.
현재 HTMLVideoElement를
Picture-in-Picture 창(requestPictureInPicture())에 넣는 웹 API가 존재합니다. 이로 인해 웹사이트가 맞춤형
picture-in-picture 경험(PiP)을 제공하는 데 제한이 있습니다. 우리는 항상 최상위에 표시되는 창에서 웹사이트가 전체 Document를
제공할 수 있도록 이 기능을 확장하고자 합니다.
이 새로운 창은 기존 open() 메서드를 통해 동일 출처로 빈 창을 여는 것과 비슷하지만 몇 가지 차이점이 있습니다:
-
PiP 창은 다른 창 위에 떠 있습니다.
-
PiP 창은 열었던 창보다 오래 지속되지 않습니다.
-
웹사이트는 PiP 창의 위치를 설정할 수 없습니다.
-
PiP 창은 탐색될 수 없으며, `window.history` 또는 `window.location`으로 새 문서로 변경하면 PiP 창이 닫힙니다.
2. 의존성
이 명세의 IDL 조각은 Web IDL 명세에 명시된 대로, 규격을 준수하는 IDL 조각으로 해석해야 합니다. [WEBIDL]
3. 보안 고려사항
3.1. 보안 컨텍스트
이 API는 [SECURE-CONTEXTS]에 한정됩니다.
3.2. 스푸핑
사용자 에이전트는 DocumentPictureInPicture
창에서 악의적인 웹사이트가 다른 창 위에 띄우는 기능을 악용하여 다른 웹사이트나 시스템 UI를 스푸핑하는 것을 막기 위해 충분한 UI를 제공해야 합니다.
3.2.1. 위치 지정
사용자 에이전트는 웹사이트가 창의 위치를 설정하지 못하도록 해야 하며, 이는 사용자가 창을 다른 페이지의 UI 일부로 오인할 수 있는 위치를 일부러 지정할 수 없게 하기 위함입니다. 특히
moveTo()와
moveBy()
API는 document picture-in-picture 창에서 비활성화되어야 합니다.
3.2.2. 출처 가시성
사용자 에이전트는 DocumentPictureInPicture
창을 제어하는 출처를 사용자에게 항상 명확히 보여주어야 하며, 사용자가 콘텐츠가 어디서 오는지 인식할 수 있어야 합니다. 예를 들면, 사용자 에이전트는 창의 제목 표시줄에 웹사이트의 출처를
표시할 수 있습니다.
3.2.3. 최대 크기
사용자 에이전트는 항상 최상위에 표시되는 창으로 화면 전체를 덮거나 PiP 창에 사용자가 갇히는 상황, 그리고 사용자의 데스크탑이 스푸핑되는 것을 막기 위해 document picture-in-picture 창의 최대 크기를 제한해야 합니다.
3.2.4. 전체화면
사용자 에이전트는 문서 PiP(Picture-in-Picture) 창이
전체화면으로 전환되지 않도록 막아야 하며, 그 근거는
최대 크기 제한에 대해 언급한 논리와 같다.
즉, requestFullscreen()
API가
문서의 PiP 창에 있는 모든 요소에서 비활성화되어야 함을 의미한다.
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 = 0; [width EnforceRange ]unsigned long long = 0;height 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 getter의 단계는:
-
this의 documentPictureInPicture API를 반환한다.
각 DocumentPictureInPicture
객체는 연관된
마지막으로 연 창(last-opened window)을 갖는다.
이는 Window
객체로, 기본값은 null이고
requestWindow() 메서드 단계에서 지정된다.
window getter의 단계는:
requestWindow(options) 메서드 단계:
-
Document Picture-in-Picture 지원 이
false이면, "NotSupportedError"DOMException을 발생(throw)시킨다. -
this의 relevant global object의 navigable이 최상위(top-level) traversable이 아니라면, "
NotAllowedError"DOMException을 throw한다. -
this의 relevant global object의 navigable의 Is Document Picture-in-Picture 불린값이
true이면 "NotAllowedError"DOMException을 throw한다. -
this의 relevant global object가 임시 활성화(transient activation)를 가지고 있지 않으면, "
NotAllowedError"DOMException을 throw한다. -
options["
width"] 가 존재 && 0 초과이고, options["height"] 가 없거나 0이면RangeError를 throw한다. -
options["
height"] 가 존재 && 0 초과이고, options["width"] 가 없거나 0이면RangeError를 throw한다. -
사용자 활성화 소모(Consume user activation)를 this의 relevant global object로 전달해 실행한다.
-
win을 this의 마지막으로 연 창으로 한다. win이
null이 아니고 win의 closed 속성이false라면, close win의 navigable을 실행한다. -
선택적으로 user agent는 기존 PiP 창을 모두 닫을 수 있다.
-
pip traversable를 새 top-level traversable 생성 결과(인자: this의 relevant global object의 navigable의 active browsing context, "
_blank")로 구한다.
생성된 Document의
URL은 `about:blank`이지만,
document base URL은 requestWindow()
를 호출한 호출자의 값으로 폴백된다.
일부 브라우저는 일반 `about:blank` 팝업에 이 폴백을 구현하지 않으니,
whatwg/html#421
논의를 참고할 것. 구현자는 문서 PiP 창에서는 반드시 이 상속이 이뤄지도록 구현하여 상호운용성 문제를 방지해야 한다.
-
pip traversable의 active document의 mode를 this의 relevant global object의 연결된 Document의 mode 값으로 설정한다.
-
pip traversable의 Is Document Picture-in-Picture 불린값을
true로 설정한다. -
options["
width"]가 존재, 0 초과이면:-
필요하다면 options["
width"] 값이 너무 크거나 작으면 윈도우 사이즈가 사용자 친화적이도록 보정(clamp)하거나 무시할 수 있다. -
필요하다면 pip traversable의 active browsing context의 window의 뷰포트 좌우 픽셀 거리를 options["
width"] 픽셀로 설정할 수 있다.
-
-
options["
height"]가 존재, 0 초과이면:-
필요하다면 options["
height"] 값이 너무 크거나 작으면 윈도우 사이즈가 사용자 친화적이도록 보정(clamp)하거나 무시할 수 있다. -
필요하다면 pip traversable의 active browsing context의 window의 뷰포트 상하 픽셀 거리를 options["
height"] 픽셀로 설정할 수 있다.
-
options["preferInitialWindowPlacement"]
가 존재하고 true라면, user agent는 PiP 창의 이전 위치/크기를 무시하고 13, 14단계와 유사한 동작을 더 우선적으로 적용할 수 있다.
-
options["
disallowReturnToOpener"] 가 존재하고true면, user agent는 PiP 창에서 opener 창으로 돌아갈 수 있는 UI 수단을 표시하지 않는다.
비디오 및 문서 PiP 모두에서 user agent는 원래 페이지로 돌아가면서 PiP 창을 닫는 버튼을 자주 표시한다.
대부분의 경우(특히 메인 문서로 영상을 돌려보내는 비디오 PiP 창)의 동작은 합당하지만,
문서 PiP에는 항상 적합하지 않을 수 있다.
disallowReturnToOpener
는 웹사이트가 해당 문서 PiP 경험에 이를 허용할지의 힌트를 user agent에 제공한다.
-
pip traversable의 active browsing context의 window를 다른 창 위에 뜨도록(float) 설정한다.
-
this의 마지막으로 연 창을 pip traversable의 active window로 설정한다.
-
글로벌 작업을 큐에 넣는다(queue a global task). DOM 조작 작업 소스 및 this의 relevant global object를 지정해 이벤트(enter)를 발생시킴(fire) (이벤트 타입:
enter,DocumentPictureInPictureEvent, this에서,window속성은 pip traversable의 active window로 초기화한다). -
이 Promise(프로미스)를 pip traversable의 active window로 resolve해서 반환한다.
창의 크기는 웹사이트에서 지정 가능하지만, 초기 위치는 user agent에 달려 있다.
enter-
PiP 창이 열렸을 때
DocumentPictureInPicture에 발생(fire)된다.
6. 개념
6.1. 문서 Picture-in-Picture 지원
각 사용자 에이전트는 문서 Picture-in-Picture 지원 불린 값을 가지며, 그 값은 구현에 따라 다름(사용자 설정에 따라 달라질 수 있음)입니다.
6.2. DocumentPictureInPicture 창
각 최상위(traversable)는
문서 PiP 여부(Is
Document Picture-in-Picture)
불린값을 가진다. 이 값의 기본값은 false이며,
requestWindow() 단계에서
true로 설정될 수 있다.
사용자 에이전트는 일반적으로 최소화되거나 사용자에게 보이지 않는 창의 렌더링을 일시 중지하거나 스크립트 실행을 제한한다(throttle). 문서 PiP 창이 열려 있을 때 오프너(opener) 창이 문서 PiP 창의 콘텐츠에 영향을 주는 스크립트를 실행할 수 있다.
구현자에게는 이런 오프너 창의 쓰로틀링이 미칠 영향을 반드시 검토할 것을 권장하며, 개발자에게는 애플리케이션 로직을 PiP 창 내부에서 실행할 것을 권장한다.
6.3. 문서 Picture-in-Picture 창 닫기
이것을 definitely close 항목에 컨센서스가 충분해지면 합치세요.
definitely close 의 2단계를 다음과 같이 수정하세요. "If the result of checking if unloading is user-canceled for toUnload is not "continue", then return." →
-
traversable의 문서 PiP 여부(Is Document Picture-in-Picture) 불린값이 true라면, 이 단계를 건너뛴다. 그렇지 않으면 unloading이 사용자에 의해 취소됐는지 검사 결과가 "continue"가 아니면 반환한다.
6.4. 기존 PiP 창 닫기
기존 picture-in-picture 창 닫기 방법:
-
사용자 에이전트의 최상위 이동 가능 객체 집합의 각 top-level traversable에 대해:
-
top-level traversable의 문서 Picture-in-Picture 여부가
true라면 close top-level traversable를 실행한다. -
top-level traversable의 활성 문서의 pictureInPictureElement 값이
null이 아니면 PiP 종료 알고리즘을 top-level traversable의 활성 문서로 실행한다. -
top-level traversable의 활성 문서의 하위 이동 가능 객체들 각각에 대해:
-
navigable의 활성 문서의 pictureInPictureElement 값이
null이 아니면 PiP 종료 알고리즘을 navigable의 활성 문서로 실행한다.
-
-
6.5. 단일 PiP 창
어떤 최상위 이동 가능 객체도 한 번에 하나의 문서 picture-in-picture 창만 열어야 합니다. 최상위 이동 가능 객체의 활성 창의 documentPictureInPicture API의 last-opened window가 null이 아닐
때
또 다른 문서 picture-in-picture 창을 열려고 할 경우, 사용자 에이전트는 기존 last-opened window를 requestWindow() 단계에 따라서 닫아야 합니다.
하지만, 모든 최상위 이동 가능 객체에 대해 PiP 창을 하나만 허용할지는 구현과 플랫폼에 따라 다릅니다.
따라서 Picture-in-Picture 요청이 있을 때,
최상위 이동 가능 객체 중 문서 Picture-in-Picture 여부가 true이거나
활성 문서의 pictureInPictureElement가
null이 아닐 때의 동작은
구현에 맡깁니다: 사용자 에이전트는 기존 picture-in-picture 창 닫기를 할 수도 있고, 여러
PiP 창을 생성할 수도 있습니다.
6.6. 원본 또는 PiP 문서가 파기될 때 PiP 창 닫기
연관된 문서 Picture-in-Picture 창 닫기 방법
(주어진 Document
document):
-
navigable을 document의 node navigable로 한다.
-
navigable가 최상위 이동 가능 객체가 아니면 이 단계를 중단한다.
-
navigable의 문서 Picture-in-Picture 여부가
true이면 close navigable를 실행하고, 단계를 중단한다. -
win을 navigable의 활성 창의 documentPictureInPicture API의 last-opened window로 한다.
-
win이
null이 아니고 win의 closed 속성이false이면 close win의 navigable를 실행한다.
합의가 충분하면 destroy에 병합하십시오.
destroy의 끝에 10단계 추가:
-
연관된 문서 Picture-in-Picture 창 닫기를 document로 실행한다.
이 단계는 열린 문서 Picture-in-Picture 창이 있는 페이지가 닫히면 PiP 창도 함께 닫히도록 보장합니다.
6.7. 원본 또는 PiP 문서가 탐색될 때 PiP 창 닫기
이것을 navigate 항목에 컨센서스가 충분해지면 합치세요.
navigate 의 23.3단계를 다음과 같이 수정하고, 그 바로 뒤에 23.4단계를 삽입하세요:
-
글로벌 작업을 큐에 넣는다(Queue a global task) navigation and traversal task source 를 사용하여 navigable의 active window에서 navigable의 active document와 연관된 모든 Document Picture-in-Picture 창을 닫기를 navigable의 active document로 전달하여 문서 및 하위 문서 중단(abort a document and its descendants) 를 실행한다.
-
navigable이 최상위 traversable이면서 문서 PiP 여부(Is Document Picture-in-Picture) 불린값이
true이면, 이 단계를 중단(abort these steps)한다.
이 변경은 Document Picture-in-Picture 창이 열린 상태에서 페이지를 탐색하면 PiP 창도 함께 닫히도록 보장한다. 또한, Document Picture-in-Picture 창 자체의 문서를 탐색할 경우에도 PiP 창이 닫히도록 한다.
6.8. PiP 창 크기 조정
문서 picture-in-picture 창을 프로그래밍적으로 크기 조정하는 것은 유용하지만, 항상 최상위로 띄우는 성격상 윈도우 크기를 너무 자유롭게 조정하면 사용자에게 불편하거나 방해가 되는 식으로 남용될 수 있습니다. 이런 문제를 완전히 막지 않으면서도 window resize API의 활용을 제한하려면 document picture-in-picture 창에서는 해당 API가 사용자 제스처를 소모하도록 해야 합니다.
합의가 충분할 때 resizeTo()
에 병합하십시오.
resizeTo()
3단계 이후에 다음 단계를 추가하세요. "만약 target이 사용자 동작이 아닌 스크립트로 생성된 보조 브라우징 컨텍스트가 아니라면 반환한다.":
-
target의 최상위 이동 가능 객체의 문서 Picture-in-Picture 여부가
true라면:-
this의 relevant global object가 순간 활성화를 갖지 않으면 "
NotAllowedError"DOMException을 throw함. -
사용자 활성화 소모를 this의 relevant global object에 실행.
-
합의가 충분하면 resizeBy()
에 병합하십시오.
3단계 이후에 resizeBy()
에 다음 단계를 추가하세요. "만약 target이 사용자 동작이 아닌 스크립트로 생성된 보조 브라우징 컨텍스트가 아니라면 반환한다.":
-
target의 최상위 이동 가능 객체의 문서 Picture-in-Picture 여부가
true라면:-
this의 relevant global object가 순간 활성화를 갖지 않으면 "
NotAllowedError"DOMException을 throw함. -
사용자 활성화 소모를 this의 relevant global object에 실행.
-
6.9. PiP 창 이동하기
§ 3.2.1 위치 지정(Positioning)에서 설명한 스푸핑(spoofing) 방지를 위해, 문서 PiP(window)의 moveTo()
와 moveBy()
API를 비활성화(disable)합니다.
컨센서스가 충분해지면 이 내용을 moveTo()
와 moveBy()
단계에 통합하세요.
moveTo()
의 3단계 이후에 다음 단계를 추가하세요:
"만약 target이 스크립트에 의해 생성된(사용자 액션이 아닌)
보조 브라우징 컨텍스트(auxiliary browsing context)가 아니라면 return."
-
만약 target의 최상위(traversable)의 문서 PiP 여부 불린값이
true라면, return.
moveBy()
의 3단계 이후에 다음 단계를 추가하세요:
"만약 target이 스크립트에 의해 생성된(사용자 액션이 아닌)
보조 브라우징 컨텍스트(auxiliary browsing context)가 아니라면 return."
-
만약 target의 최상위(traversable)의 문서 PiP 여부 불린값이
true라면, return.
6.10. 오프너 창 포커싱
PiP 창이 오프너 탭에 다시 포커스를 맞출 수 있다면(예: PiP 창의 크기가 작아서 사용자의 경험에 맞지 않는 경우 등) 유용할 수 있습니다.
focus()
API를 수정하여 PiP 창에서 오프너를 포커싱할 때 시스템 레벨 포커스를 받을 수 있게 허용합니다.
컨센서스가 충분해지면 이 내용을 focus()
에 통합하세요.
focus()
의 4단계 이후에 다음 단계를 추가하세요:
"포커싱 단계(focusing steps)를 current로 실행."
-
만약 current가 최상위 traversable이라면:
-
pipWindow를 current의 active window의 documentPictureInPicture API의 last-opened window로 한다.
-
만약 pipWindow가
null이 아니고 pipWindow의 relevant global object가 transient activation을 가지고 있다면:-
사용자 활성화 소모를 pipWindow의 relevant global object로 실행한다.
-
current에 시스템 포커스(system focus)를 부여한다.
-
-
오프너에 시스템 포커스를 주는 것이 반드시
문서 picture-in-picture 창을 닫아야 함을 의미하지는 않습니다.
만약 포커싱 후에 문서 PiP 창을 닫길 원한다면 웹사이트는 PiP 창에서 close()
를 호출해서 닫을 수 있습니다.
6.11. CSS display-mode
CSS display mode 미디어 특성 picture-in-picture를 사용하면 웹 개발자는 웹앱의 일부 또는 전체가 PiP 모드로 표시될 때만 적용되는 특정 CSS 규칙을 작성할 수 있습니다.
6.12. 사용자 활성화 전파
문서 PiP 창의 특성상, 창 내부 버튼의 이벤트 핸들러가 실제로는 오프너 컨텍스트에서 실행되는 경우가 많습니다. 이는 해당 창이 activation consuming API 를 호출하는 것을 불편하게 만드는데, 때때로 PiP 창에는 transient activation이 있지만 오프너에는 없기 때문입니다.
이 문제를 쉽게 하기 위해, activation notification 단계에, 문서 PiP 창에서 사용자 활성화를 발생시킬 때 오프너에서도 사용자 활성화를 트리거하도록 업데이트합니다. 추가로, 오프너에서 사용자 활성화가 트리거될 때 문서 PiP 창 내의 동일 출처 프레임이 하위 프레임 전파와 유사하게 활성화되도록 합니다.
컨센서스가 충분해지면 이 내용을 activation notification 단계에 통합하세요.
activation notification 4단계 이후에 다음 3단계를 추가하세요: "Extend windows with the active window of each of document’s descendant navigables, filtered to include only those navigables whose active document’s origin is 같은 출처(same-origin)인 경우":
-
만약 document의 node navigable의 최상위(traversable)의 문서 PiP 여부 불린값이
true라면, Extend windows에 document의 node navigable의 최상위(traversable)의 active browsing context의 opener browsing context의 active window를 추가한다. -
document picture-in-picture window를 document의 node navigable의 최상위(traversable)의 active window의 documentPictureInPicture API의 last-opened window로 둔다.
-
만약 document picture-in-picture window가
null이 아니라면, extend windows에 document picture-in-picture window의 연관된 문서(associated document)의 하위 내비게이션(descendant navigables) 각각의 active window를 추가한다. 단, 각 navigable의 active document의 origin이 document picture-in-picture window의 연관된 문서의 origin과 동일 출처(same origin)인 것만 포함한다.
또, 활성화가 제대로 소모되어(opener에서 한 번, PiP 창에서 한 번 두 번 실행되지 않도록) 해야 합니다. 이를 위해 consume user activation 단계에 다음 동작을 추가합니다: PiP 창의 활성화 소모 시 오프너에서도, 오프너 활성화 소모 시 연관된 PiP 창에서도 소모가 일어납니다.
컨센서스가 충분해지면 이 내용을 consume user activation에 통합하세요.
consume user activation 의 3단계 이후에 다음 3단계를 추가하세요: "Let navigables be the inclusive descendant navigables of top’s active document."
-
만약 top의 문서 PiP 여부 불린값이
true라면, Extend navigables에 top의 active browsing context의 opener browsing context의 active document의 포함된 하위 navigables를 추가한다. -
document picture-in-picture window를 top의 active window의 documentPictureInPicture API의 last-opened window로 둔다.
-
만약 document picture-in-picture window가
null이 아니라면, Extend navigables에 document picture-in-picture window의 연결된 문서(associated document)의 포함된 하위 navigables를 추가한다.
6.13. PiP 창 전체화면
§ 3.2.4 전체화면(Fullscreen)에서 언급했듯, 문서 PiP 창은 전체화면 진입이 허용되지 않습니다. 이 제약을 강제하기 위해 fullscreen is supported 정의를 업데이트합니다.
컨센서스가 충분해지면 이 내용을 fullscreen is supported 에 통합하세요.
fullscreen is supported의 "if there is no previously-established user preference, security risk, or platform limitation." 부분을 다음으로 교체하세요.
fullscreen is supported는 Window
window가 아래 모든 조건을 만족할 때 true입니다:
-
사용자 설정, 보안 위험, 플랫폼 제한이 사전에 존재하지 않는다.
-
window의 navigable의 최상위 traversable의 문서 PiP 여부 불린값이
false이다.
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
// picture-in-picture 창을 가리키는 핸들. let pipWindow= null ; function enterPiP() { const player= document. querySelector( '#player' ); // 창을 비디오에 맞는 크기로 지정. 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 창에서 이벤트 리스닝
더 개선된 picture-in-picture 경험을 위해 사이트에서는 버튼 등 사용자 입력에 반응해야 하는 컨트롤을 직접 정의하고 싶을 때가 많습니다.
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 종료
웹사이트는 사용자가 창의 닫기 버튼을 직접 클릭하지 않아도
DocumentPictureInPicture
Window
를 닫고 싶을 수 있습니다. 이 경우, 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 창 프로그래밍적 크기 조정
문서 picture-in-picture 창에서는 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. Opener 탭으로 돌아가기
focus()
API로 picture-in-picture 창에서 오프너 탭을 사용자 제스처로 포커스할 수 있습니다:
const returnToTabButton= pipWindow. document. createElement( 'button' ); returnToTabButton. textContent= '오프너 탭으로 이동' ; returnToTabButton. addEventListener( 'click' , () => { window. focus(); }); pipWindow. document. body. append( returnToTabButton);
7.8. CSS picture-in-picture display mode 사용
아래 예시는 PiP 창에서 body 엘리먼트의 margin을 제거하고, 타이틀의 폰트 크기를 더 작게 하여 PiP 창의 컨텐츠에 더 잘 맞추는 방법을 보여줍니다:
@media all and( display-mode: picture-in-picture) { body{ margin : 0 ; } h1{ font-size : 0.8 em ; } }
7.9. Opener로 돌아가기 버튼 숨기기
대부분의 사용자 에이전트는 비디오 및 문서 picture-in-picture 창에서 오프너로 돌아가고 창을 닫는 버튼을 표시하는데, 모든 사이트의 문서 PiP UX에 이 버튼이 항상 적합하지는
않습니다. disallowReturnToOpener
옵션을 사용하여 버튼을 숨길 수 있습니다.
await documentPictureInPicture. requestWindow({ disallowReturnToOpener: true });
7.10. 초기 창 배치 우선
문서 picture-in-picture 창이 열려 있을 때 사용자가 직접 크기 조정/위치 옮기기를 할 수도 있습니다. 창을 닫았다가 나중에 다시 열면 사용자 에이전트는 이전 위치와 크기를 새로운 창의 배치 힌트로 사용할 수 있습니다.
사이트는 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께 본 명세와 그 논의를 통해 주신 의견과 기여에 감사드립니다.