1. 소개
이 절은 비규범적이다.
이 명세는 스크립트가 제한된 API를 호출할 수 있는 자신의 능력을 자신이 신뢰하는 다른 브라우징 문맥에 위임할 수 있는 메커니즘을 정의한다. 여기서 초점은 위임된 기능을 시간 제약 방식으로 대상 브라우징 문맥에 노출하는 동적 위임 메커니즘이다.
1.1. 기능 위임이란 무엇인가?
웹의 많은 기능은 JS에서 제한된 방식으로 사용할 수 있다. 예를 들어:
-
대부분의 브라우저는 사용자가 최근에 페이지와 상호작용했거나, 브라우저가 해당 페이지의 오리진에서 팝업을 열도록 허용한 경우에만
Window.open()을 통해 팝업을 허용한다. -
샌드박스 처리된 iframe은 특정 샌드박스 속성이나 프레임 내의 사용자 상호작용 없이는 자기 자신을 전체 화면으로 만들 수 없다 (
requestFullscreen()를 통해서도 마찬가지다).
기능 위임이란 어떤 프레임이 제한된 API를 호출할 수 있는 자신의
능력을 동적으로 포기하고, 신뢰할 수 있는 다른 (하위)프레임에 그 능력을
이전할 수 있게 하는 것을 의미한다. 여기서 "동적"이라는 말은 위임의 효과가
위임되는 기능에 의해 정의된 제한된 시간 동안만 지속된다는 뜻이다. 이는
브라우징
문맥에 기능을 정적으로(로드 시점에) 노출하는 것, 즉 iframe
allow
속성을 통해 기능이 하위 프레임에 시간 제약 없이 노출되는 것과는 다르다.
1.2. 위임 시작과 기능 사용
기능 위임이 효과를 가지려면 두 개의 구별되는 단계가 필요하다. 첫 번째 단계는 "시작"으로, 하나의 브라우징 문맥이 다른 브라우징 문맥에 특정 기능이 위임되었음을 알리는 것이다. 시작 이후, 두 번째(즉 수신자) 브라우징 문맥은 위임된 기능을 "사용"하게 되며, 이는 일반적으로 기능이 정의한 메서드를 호출하는 것을 의미한다. 여기의 기능 위임 명세는 두 번째 단계에서 사용되는 API 인터페이스를 정의하지 않지만, API의 내부 동작은 재정의한다.
이 때문에 이 명세는 두 개의 구별되는 부분으로 구성된다. 즉 시작 단계를 위한 API를 정의하는 부분과, 하나의 특정 "사용자" API에 대한 위임 동작을 정의하는 부분이다. 두 번째 부분에서 이 명세는 Payment Request API에 필요한 동작 변경에 초점을 맞추며, 이는 앞으로 기능 위임을 활용할 다른 API에서의 유사한 변경을 위한 지침 역할을 하게 된다.
1.3. 일시적 가용성
위에서 언급한 두 단계는 모두 본질적으로 시간 제약을 가진다:
-
시작 단계는 활성화 소비 단계이므로, 이 단계는 최근 사용자 활성화 이후에만 허용된다. 또한 여기서 사용자 활성화를 소비하면 위임 메커니즘을 사용자 활성화당 한 번 이상 사용할 수 없다는 것이 보장된다. 이는 위임된 API에 대한 사용자 활성화 제한을 사실상 우회하기 위해 여러 프레임에 반복적으로 위임을 시도하는 것과 같은 기능 위임의 악의적 사용을 방지한다.
-
시작 단계가 성공적으로 완료된 뒤, 위임된 API는 대상 브라우징 문맥에서 몇 초 동안만 사용할 수 있게 된다. 여기서 정확한 시간 제한은 위임된 API가 자신의 명세에서 위임 동작을 어떻게 정의하는지에 따라 달라진다. 자체 시간 제한을 정의하지 않는 API의 경우, 기본 제한은 사용자 활성화 만료와 동일하다.
2. 예제
show()를
호출할 수 있는 기능을 위임하려는 경우, 위임할 기능을 지정하는 추가
옵션과 함께 하위 프레임에 메시지를
게시한다:
window. onclick= () => { targetWindow. postMessage( 'a_message' , { delegate: "payment" }); };
메시지를 수신하면, 해당 하위 프레임은 사용자
활성화를 받지 않았더라도 show()를
사용할 수 있다:
window. onmessage= () => { const payRequest= new PaymentRequest(...); const payResponse= await payRequest. show(); ... }
3. 기능 위임 시작
브라우징 문맥이 기능을 다른 브라우징
문맥에 위임하려는 경우, 해당 기능을 지정하는 delegate라는
추가 WindowPostMessageOptions와
함께
두 번째 브라우징 문맥에 메시지를 게시한다. 이
옵션의 값은 feature-identifier여야
한다.
이 옵션은 그 값이 사용자
에이전트가 지원하는 어떤 features에도
해당하지 않으면 무시되어야 한다.
3.1. HTML 명세에 대한 몽키 패치
WindowPostMessageOptions
IDL 정의는 다음과 같은 추가 필드를 포함하게 된다:
DOMString? delegate;
window post message 알고리즘에서, 다음 단계:
Let transfer be options["transfer"].
뒤에는 다음 두 개의 추가 단계가 이어진다:
-
delegate를 options["delegate"]라고 하자.
-
delegate가 null이 아니면:
-
사용자 에이전트가 delegate가 나타내는 feature의 위임을 지원하지 않으면, "NotSupportedError" DOMException을 던진다.
-
targetWindow의 associated Document가 delegate가 나타내는 feature를 allowed-to-use 할 수 없으면, "NotAllowedError" DOMException을 던진다.
-
targetOrigin이 단일 U+002A ASTERISK 문자(*)이면, "NotAllowedError" DOMException을 던진다.
targetOrigin의 기본값은 "/"이며, 메시지를 same-origin 대상으로 제한한다. "*"가 아닌 문자열을 사용해야 한다는 추가 요구사항은 cross-origin 메시지가 의도된 특정 오리진을 지정해야 함을 의미한다. -
source를 incumbentSettings의 전역 객체라고 하자.
-
source가 일시적 활성화를 가지고 있지 않으면, "NotAllowedError" DOMException을 던진다.
-
source에서 사용자 활성화를 소비한다.
-
4. 위임된 기능 추적
브라우징 문맥에 위임된 기능은
Window.DELEGATED_CAPABILITY_TIMESTAMPS라는 이름의 맵을 사용하여 추적된다.
어떤 기능이 Window에
위임될 때마다,
기능을 나타내는 feature-identifier와
같은 키와,
현재 DOMHighResTimeStamp와
같은 값을 가진
항목이 DELEGATED_CAPABILITY_TIMESTAMPS에 추가된다.
맵에 이미 같은 키에 대한 항목이 있으면, 기존 값은 현재 DOMHighResTimeStamp로
갱신된다.
4.1. HTML 명세에 대한 몽키 패치
window post message 알고리즘 바로 앞에, 다음과 같은 새 문단이 삽입된다:
브라우징 문맥에 위임된 기능을 추적하기 위해, 각
Window는 맵을 가지며, 이는 feature-identifier에서DOMHighResTimeStamp로 가는 DELEGATED_CAPABILITY_TIMESTAMPS라고 불린다. 이 맵은 빈 맵으로 초기화된다.
window post message 알고리즘에서, 현재 Step 8에 두 개의 추가 하위 단계가 추가된다. 첫 번째 추가 하위 단계는 다음 하위 단계 뒤에 삽입된다:
Queue a global task ...
Let origin be the serialization of incumbentSettings’s origin.
다음과 같다:
-
Queue a global task ... (변경 없음)
-
delegate를 options["delegate"]라고 하자.
-
두 번째 추가 하위 단계는 다음 하위 단계 뒤에 삽입된다:
Queue a global task ...
Let newPorts be a new frozen array consisting of ...
다음과 같다:
-
Queue a global task ... (변경 없음)
-
Let newPorts be a new frozen array consisting of ... (번호 매김을 제외하고 변경 없음)
-
delegate가 null이 아니고, 사용자 에이전트가 delegate 위임을 지원하면, DELEGATED_CAPABILITY_TIMESTAMPS[delegate]를 현재 고해상도 시간으로 설정한다.
-
5. 위임된 기능 동작 정의
위임된 동작을 정의하는 모든 기능은 해당 기능에 적절한 방식으로 Window.DELEGATED_CAPABILITY_TIMESTAMPS의 해당 항목을 사용한다.
아래는 하나의 특정 기능에 필요한 명세 변경이다.
5.1. Payment Request 명세에 대한 몽키 패치
show()
알고리즘에서,
위임된 동작을 구현하기 위해
다음 단계들이 대체된다:
두 단계:
If the relevant global object of request does not have transient activation:
Return a promise rejected with a "SecurityError" DOMException.
Consume user activation of the relevant global object.
는 다음 세 단계로 대체된다:
-
request의 관련 전역 객체가 일시적 활성화를 가지고 있지 않고, 관련 전역 객체의 DELEGATED_CAPABILITY_TIMESTAMPS["payment"] 타임스탬프가 undefined이거나 만료된 경우:
-
"SecurityError" DOMException으로 거부된 promise를 반환한다.
-
-
request의 관련 전역 객체가 일시적 활성화를 가지고 있지 않으면, 맵 항목 DELEGATED_CAPABILITY_TIMESTAMPS["payment"]를 지운다.
-
그렇지 않으면, 관련 전역 객체의 사용자 활성화를 소비한다.
5.2. Fullscreen 명세에 대한 몽키 패치
requestFullscreen()
알고리즘에서,
위임된 동작을 구현하기 위해
다음 변경이 수행된다:
Step 5의 마지막 조건:
If any of the following conditions are false, then set error to true: ...
This’s relevant global object has transient activation or the algorithm is triggered by a user generated orientation change.
은 다음으로 대체된다:
-
If any of the following conditions are false, then set error to true: ... (변경 없음)
-
this의 관련 전역 객체가 일시적 활성화를 가지고 있거나, this의 관련 전역 객체에 있는 DELEGATED_CAPABILITY_TIMESTAMPS["fullscreen"] 타임스탬프가 undefined가 아니며 만료되지 않았거나, 또는 알고리즘이 사용자 생성 orientation change에 의해 트리거된 경우.
-
Step 10 바로 앞:
Let fullscreenElements be an ordered set initially consisting of this.
에 다음 새 단계가 삽입된다:
-
this의 관련 전역 객체가 일시적 활성화를 가지고 있지 않으면, this의 관련 전역 객체에서 맵 항목 DELEGATED_CAPABILITY_TIMESTAMPS["fullscreen"]를 지운다.
-
Let fullscreenElements be an ordered set initially consisting of this. (번호 매김을 제외하고 변경 없음)
5.3. [SCREEN-CAPTURE] 명세에 대한 몽키 패치
getDisplayMedia()
알고리즘에서,
위임된 동작을 구현하기 위해
다음 변경이 수행된다:
Step 3의 조건:
If the relevant global object of this does not have transient activation, return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError.
은 다음으로 대체된다:
-
this의 관련 전역 객체가 일시적 활성화를 가지고 있지 않고, this의 관련 전역 객체에 있는 DELEGATED_CAPABILITY_TIMESTAMPS["display-capture"] 타임스탬프가 undefined이거나 만료된 경우,
name속성 값이InvalidStateError인 DOMException 객체로 거부된 promise를 반환한다.