Copyright © 2024 the Contributors to the WebHID API Specification, published by the Web Platform Incubator Community Group under the W3C Community Contributor License Agreement (CLA). A human-readable summary is available.
본 문서는 Human Interface Device (HID) 프로토콜을 지원하는 장치에 접근할 수 있도록 하는 API에 대해 설명합니다.
이 명세는 Web Platform Incubator Community Group에서 발행하였습니다. W3C 표준이 아니며, W3C 표준 트랙에도 포함되어 있지 않습니다. W3C Community Contributor License Agreement (CLA)에 따라 일부 옵트아웃이 제한적으로 가능하며, 추가 조건이 적용될 수 있습니다. W3C 커뮤니티 및 비즈니스 그룹에 대해 더 알아보세요.
본 명세에 대한 논의는 GitHub Issues를 우선적으로 이용해 주세요.
이 절은 비규범적(참고용)입니다.
HID(Human Interface Device, 휴먼 인터페이스 디바이스)는 사람에게 입력을 받거나 출력을 제공하는 장치 유형입니다. 또한 HID 프로토콜은 호스트와 장치 간의 양방향 통신을 표준화하여 설치 과정을 단순화하기 위해 설계된 표준을 의미하기도 합니다. HID 프로토콜은 원래 USB 기기용으로 개발되었으나, 이후 Bluetooth를 비롯한 다양한 프로토콜에서도 구현되었습니다.
웹 플랫폼은 이미 다양한 HID 장치의 입력을 지원합니다. 키보드, 포인팅 장치, 게임패드는 모두 일반적으로 HID 프로토콜로 구현됩니다. 그러나 이러한 지원은 운영 체제의 HID 드라이버가 HID 입력을 고수준 입력 API로 변환하는 데 의존합니다. 호스트의 HID 드라이버로 잘 지원되지 않는 장치는 웹페이지에서 접근할 수 없는 경우가 많습니다. 마찬가지로, 대부분의 장치에서 호스트의 HID 드라이버가 지원하지 않는 출력도 접근할 수 없습니다.
관련 문서로 설명서(Explainer)도 참고하세요.
이 절은 비규범적(참고용)입니다.
가장 일반적인 HID 입력 장치 유형들은 기존 고수준 입력 API를 통해 이미 웹 플랫폼에서 잘 지원됩니다. 예를 들어, 마우스 입력은 PointerEvent로, 키보드 입력은
KeyboardEvent로 접근할 수 있습니다. 이러한
장치의 입력은 호스트의 기본 HID 드라이버를 사용하여 처리되며, 일반적으로 장치별 드라이버나 별도의 설정 없이 정상 작동합니다. WebHID는 이미 고수준 입력 API로 잘 지원되는
이러한 장치들을 위한 것이 아닙니다.
일부 HID 장치에 대해서는 웹 플랫폼이 장치의 일부 기능만 지원하고 다른 기능의 접근은 제한될 수 있습니다. 예를 들어, [GAMEPAD]는 대부분의 게임 컨트롤러의 입력 기능만 지원하며, LED 표시등이나 오디오와 같은 비교적 드문 기능은 지원하지 않습니다. 이런 기능들은 호스트 API에서 잘 지원되지 않으며, 사용자 에이전트에서 직접 지원을 추가하면 큰 복잡성이 초래될 수 있습니다. WebHID는 고수준 API가 완전한 기능을 제공하지 못할 경우, 애플리케이션에 대안이 될 수 있습니다.
많은 HID 장치는 어떤 웹 플랫폼 API로도 지원받지 못합니다. HID 명세에서는 가상현실 컨트롤러, 플라이트 시뮬레이터, 의료 장비 등 HID로 구현 가능하지만 지원되지 않는 매우 다양한 장치들을 기술하고 있습니다. WebHID를 통해서는 별도의 드라이버나 사용자 에이전트 수정 없이도 이러한 장치들을 사용할 수 있습니다.
HID는 호스트마다 개별 드라이버가 필요하지 않고 호스트의 범용 HID 드라이버를 사용할 수 있기 때문에 프로토타이핑과 취미용 애플리케이션에 매력적입니다. 단순화된 설치 절차는 또한 교사가 각 호스트의 시스템 설정을 변경하지 않고도 학생들이 수업 시간에 장치를 손쉽게 사용할 수 있게 해 줍니다. 웹 플랫폼을 통해 HID 장치 접근을 제공하면 특별 앱이나 드라이버가 필요한 장치도 설치 요구사항을 더 줄일 수 있습니다.
이 절은 비규범적(참고용)입니다.
HID 주변기기는 페이지에서 사용자의 명시적 동의 없이 접근해서는 안 되는 강력한 기능을 제공할 수 있습니다. 예를 들어, HID 주변기기는 환경 정보를 수집하는 센서를 탑재할 수 있습니다. 장치에 공개되거나 덮어써서는 안 되는 민감한 정보가 저장되어 있을 수 있습니다. 많은 장치는 펌웨어를 업그레이드할 수 있는 기능을 제공합니다. 운영 체제는 일반적으로 응용 프로그램의 HID 장치 접근을 제한하지 않기 때문에, 이러한 접근이 남용되면 장치가 손상되거나 장치에 저장된 데이터가 파손될 위험이 있습니다.
어떤 경우에는 장치가 페이지에서 완전히 접근할 수 없어야 하는 기능을 제공할 수 있습니다. 특정 장치는 vendor ID와 product ID로 식별하여, 열거 과정에서 해당 장치를 숨길 수 있습니다. 리포트 디스크립터는 장치가 스스로 지원 기능을 기술할 수 있도록 하며, 이 정보는 미리 알 수 없는 경우에도 특정 장치 유형 전체를 차단하는 데 사용될 수 있습니다. 예를 들어, 리포트 디스크립터 안의 HID usage 값을 검사하여, 키보드와 같은 기기는 최상위 컬렉션에 해당하는 HID usage가 포함되어 있다면 접근을 거부할 수 있습니다.
남용 방지를 위해서는, 사용자가 명시적으로 접근을 허용하기 전까지 페이지에서 장치 사용을 금지해야 합니다. 페이지는 먼저 접근을 요청해야 하며, 사용자가 기기 선택 대화 상자에서 장치를 선택한 경우에만 접근이 허용됩니다. 접근이 허용된 경우 장치가 사용 중임을 알리는 표식이 표시됩니다.
HID 프로토콜은 매우 유연해서, 이 유연성을 활용한 다양한 장치가 존재합니다. 그 결과, 장치에 접근할 수 있으면 악성 페이지가 실제 장치에 피해를 줄 수 있는 다양한 가능성이 생깁니다. HID 주변기기에서 커스텀 HID 리포트를 통해 펌웨어 업데이트가 가능한 경우도 많습니다. 제조사는 펌웨어 업그레이드가 정품 이진 파일임을 검증하도록 설계하고, 다운그레이드로 인해 보안성이 저하되지 않도록 보호해야 합니다. 신뢰할 수 없는 업그레이드를 허용하면 장치에 신규 기능이 추가되거나 전혀 다른 기기로 가장하도록 만들 수 있습니다.
예상 범위를 벗어난 입력이 제공되면 손상될 수 있는 장치도 있습니다. 예를 들어 게임패드의 진동 기능(rumble)은 페이지가 유효하지 않은 진동 명령을 보낼 경우 손상될 수 있습니다.
대체로 이러한 공격은 장치에 따라 다르므로, API 차원에서 방지할 수 없습니다.
악성 페이지가 장치에 접근하면, 경우에 따라 이 접근 권한을 이용해 호스트를 공격할 수도 있습니다. 특히 장치가 신뢰된 입력 이벤트를 생성할 수 있는지 여부가 중요한 문제입니다. 이런 이벤트는 사용자 의도를 대변하고, 더 강력한 웹 플랫폼 기능에 접근하는데 사용될 수 있습니다.
마우스와 키보드는 일반적으로 HID 주변기기로 구현되어 있으며, 신뢰된 입력을 생성할 수도 있습니다. HID 키보드나 마우스에는 시퀀스를 저장하여 나중에 동작을 재생할 수 있도록 하는 프로그래머블 매크로와 같은 고급 기능이 있을 수 있습니다. 제조사는 악성 앱이 예상치 못한 입력 시퀀스으로 장치를 재프로그램하지 못하도록, 매크로 재생이 사용자의 명시적 동의 없이 실행되지 않도록 신중하게 기능을 설계해야 합니다.
보안 및 개인정보 보호 위험을 줄이기 위해, 사용자가 장치 접근을 요청할 때 반드시 선택 대화상자가 표시되는 플로우를 구현할 것을 권장합니다. 페이지가 HID 장치 접근을 요청하면 사용자 에이전트는 페이지가 장치 접근을 요청함을 알리고, 현재 연결되어 있는 HID 장치 목록을 보여주며, 사용자가 페이지에 노출할 장치를 선택할 것을 요구하는 대화상자를 보여주어야 합니다. 실수로 잘못 클릭할 가능성을 줄이기 위해 대화상자는 한 번의 상호작용으로 장치 목록에서 장치를 선택하고, 두 번째 상호작용으로 선택을 확정 및 대화상자를 닫도록 설계해야 합니다.
WebIDL[Exposed=(DedicatedWorker,ServiceWorker,Window), SecureContext]
interface HID : EventTarget {
attribute EventHandler onconnect;
attribute EventHandler ondisconnect;
Promise<sequence<HIDDevice>> getDevices();
[Exposed=Window] Promise<sequence<HIDDevice>> requestDevice(
HIDDeviceRequestOptions options);
};
사용자 에이전트의 구현은
hid 속성을 조건부로 노출하도록 선택할 수 있으며,
이는 Navigator의
관련 전역
객체가
ServiceWorkerGlobalScope인
경우에 해당합니다.
예를 들어 [browserext]의
브라우저 확장 컨텍스트에서 그렇습니다.
HID 인스턴스는 다음 표에 설명된 내부 슬롯과 함께 생성됩니다:
| 내부 슬롯 | 초기값 | 설명(비규범적) |
|---|---|---|
| [[devices]] | 비어 있는 순서 있는 집합 |
시스템에서 현재 사용 가능한
HID 인터페이스를 나타내는
순서 있는 집합의
HIDDevice 인스턴스입니다
|
onconnect
속성
onconnect는
이벤트 핸들러
IDL 속성으로,
connect 이벤트 유형에
해당합니다.
ondisconnect
속성
ondisconnect는
이벤트 핸들러
IDL 속성으로,
disconnect 이벤트
유형에 해당합니다.
시스템에서 HID 인터페이스가 사용 가능해지면 다음 단계를 수행합니다:
HID 인터페이스는 HID 프로토콜을 통해 장치와 통신하는 데 사용할 수 있는 논리적 인터페이스입니다. 각 HID 인터페이스는 해당 인터페이스가 지원하는 리포트 집합을 설명하는 정확히 하나의 리포트 서술자(report descriptor)를 가집니다. 하나의 물리적 장치는 둘 이상의 HID 인터페이스를 노출할 수 있습니다. 여러 HID 인터페이스를 가진 장치는 각 인터페이스마다 이 단계를 한 번씩 실행합니다.
예를 들어 USB 장치는 여러 인터페이스의 모음으로 구현되며, 각 인터페이스는 인터페이스 클래스를 지정할 수 있습니다. HID 클래스를 사용하는 여러 USB 인터페이스가 있는 경우, 각 인터페이스는 별도의 HID 인터페이스로 열거됩니다.
HIDDevice로 둔다.
[[vendorId]]를
해당 인터페이스를 노출한 장치의 vendor ID로 설정한다.
[[productId]]를
해당 인터페이스를 노출한 장치의 product ID로 설정한다.
[[productName]]을
해당 인터페이스를 노출한 장치의 product name으로 설정한다.
[[collections]]을
리포트 서술자 파싱의 결과로 설정한다.
Window
객체라면
그 객체의 hid getter를 호출한 결과로 하고,
그렇지 않으면 WorkerNavigator
객체의
hid getter를 호출한 결과로 한다.
[[devices]]에.
requestDevice() 호출 결과로
사용자가 사이트가 device에 접근하는 것을 허용했다면,
관련 전역 객체
상에서
HID device task source를 사용해
글로벌 태스크를 큐잉하고,
이벤트를 발행한다.
이벤트 이름은 connect이며 대상은 hid이다.
이때 HIDConnectionEvent를 사용하고,
그 device 속성은 device로 초기화한다.
리포트 서술자를 파싱하려면 다음 단계를 수행한다:
시스템은 일반적으로 장치가 처음 연결될 때 각 HID 인터페이스에 대한 리포트 서술자 바이트 시퀀스를 읽고 값을 시스템 레지스트리에 캐시합니다. 리포트 서술자를 사용할 수 없더라도, 시스템이 각 리포트의 레이아웃을 재구성하는 데 사용할 수 있는 정보를 제공하는 경우, 사용자 에이전트는 리포트 서술자를 파싱하는 대신 대체 알고리즘을 사용할 수 있습니다.
리포트 서술자 형식은 [USBIF-HID-CLASS]의 6.2.2 절에 설명되어 있습니다. 아래 알고리즘은 해당 절에 설명된 동일한 파싱 알고리즘을 구현하기 위한 것입니다.
리포트 서술자에는 세 가지 범주의 아이템이 있습니다. 메인 아이템(Main items)은 데이터 필드를 정의하고 그룹화하는 데 사용됩니다. 글로벌 아이템(Global items)과 로컬 아이템(Local items)은 데이터 필드를 설명합니다. 로컬 아이템은 다음 메인 아이템에만 적용되는 특성을 정의하며, 글로벌 아이템은 이후에 정의되는 모든 메인 아이템에 영향을 미치는 특성을 정의합니다.
각 아이템에는 0, 1, 2 또는 4 바이트의 해당 데이터가 들어 있는 item data field와, item data field를 저장하는 데 사용되는 바이트 수를 설명하는 bSize field가 있습니다. 또한 각 아이템에는 타입을 식별하는 태그가 있습니다.
HIDCollectionInfo의
빈 sequence로 초기화한다.
unsigned long의 빈
sequence로 초기화한다.
unitSystem"]을
"none"으로 설정한다.
unitExponent"]을
0으로 설정한다.
unitFactorLengthExponent"]을
0으로 설정한다.
unitFactorMassExponent"]을
0으로 설정한다.
unitFactorTimeExponent"]을
0으로 설정한다.
unitFactorTemperatureExponent"]을
0으로 설정한다.
unitFactorCurrentExponent"]을
0으로 설정한다.
unitFactorLuminousIntensityExponent"]을
0으로 설정한다.
logicalMinimum"]을 0으로 설정한다.
logicalMaximum"]을 0으로 설정한다.
physicalMinimum"]을 0으로 설정한다.
physicalMaximum"]을 0으로 설정한다.
메인 아이템은 [USBIF-HID-CLASS] 6.2.2.4~6.2.2.6 절에 설명되어 있습니다. Input tag, Output tag, Feature tag가 있는 아이템은 입력/출력/피처 리포트 내의 필드를 정의합니다. 메인 아이템의 item data field는 Input tag, Output tag, Feature tag 중 하나인 경우, 데이터 필드를 설명하는 여러 불리언 파라미터를 담은 비트 필드입니다. Collection tag가 있는 아이템은 현재 컬렉션 내부에 새 컬렉션을 정의하거나, 현재 컬렉션이 없을 때는 새 최상위 컬렉션을 정의합니다. 메인 아이템의 Collection tag의 item data field에는 컬렉션 타입이 들어 있습니다. End Collection tag가 있는 아이템은 현재 컬렉션을 닫습니다.
reportId"]로 둔다.
inputReports"]
— item에
Input
tag가 있는 경우
outputReports"]
— item에
Output
tag가 있는 경우
featureReports"]
— item에
Feature
tag가 있는 경우
reports에
그 reportId
속성이
reportId와 같은 아이템이 있으면,
report를 그 아이템으로 둔다.
그렇지 않으면:
HIDReportInfo
딕셔너리로 둔다.
reportId"]를
reportId로 설정한다.
items"]를
HIDReportItem의
빈 sequence로
설정한다.
items"]에.
HIDCollectionInfo
딕셔너리로 둔다.
usagePage"]를
globalState["usagePage"]로
설정한다.
usage"]를
usage로 설정한다.
type"]을
컬렉션의 타입을 나타내는 값으로 설정한다.
children"]을
빈 sequence의
HIDCollectionInfo로
설정한다.
inputReports"]을
HIDReportInfo의
빈 sequence로 설정한다.
outputReports"]을
HIDReportInfo의
빈 sequence로 설정한다.
featureReports"]을
HIDReportInfo의
빈 sequence로 설정한다.
ancestors가 비어 있으면 추가한다: collection을 collections에.
그렇지 않으면:
unsigned long의 빈
sequence로 설정한다.
글로벌 아이템은 6.2.2.7 절에 설명되어 있습니다. Usage Page tag, Logical Minimum tag, Logical Maximum tag, Physical Minimum tag, Physical Maximum tag, Unit Exponent tag, Unit tag, Report Size tag, Report ID tag, Report Count tag는 글로벌 아이템 상태 테이블의 해당 필드를 수정합니다. Push tag가 있는 아이템은 현재 글로벌 아이템 상태 테이블의 복사본을 아이템 상태 스택에 푸시합니다. Pop tag가 있는 아이템은 아이템 상태 스택의 맨 위 항목으로 글로벌 아이템 상태 테이블을 대체합니다. 아이템 상태 스택을 조작할 때 글로벌 Report ID 값은 영향을 받지 않습니다.
unsigned long으로 해석한 값으로 둔다.
usagePage"]를
value로 설정한다.
logicalMinimum"]를
value로 설정한다.
logicalMaximum"]를
value로 설정한다.
physicalMinimum"]를
value로 설정한다.
physicalMaximum"]를
value로 설정한다.
reportSize"]를
value로 설정한다.
reportId"]를
value로 설정한다.
reportCount"]를
value로 설정한다.
unitSystem"]을
"none"으로
설정
unitSystem"]을
"si-linear"로
설정
unitSystem"]을
"si-rotation"으로
설정
unitSystem"]을
"english-linear"로
설정
unitSystem"]을
"english-rotation"으로
설정
unitSystem"]을
"vendor-defined"로
설정
unitSystem"]을
"reserved"로
설정
unitFactorLengthExponent"]를
nibbles[1]로 설정한다.
unitFactorMassExponent"]를
nibbles[2]로 설정한다.
unitFactorTimeExponent"]를
nibbles[3]으로 설정한다.
unitFactorTemperatureExponent"]를
nibbles[4]로 설정한다.
unitFactorCurrentExponent"]를
nibbles[5]로 설정한다.
unitFactorLuminousIntensityExponent"]를
nibbles[6]으로 설정한다.
unitExponent"]를
item data field의
최하위 4비트를 부호 있는 2의 보수 정수로 해석한 결과로 설정한다.
unsigned long으로 해석한 값으로 둔다.
usageMinimum"]을
value로 설정한다.
usageMaximum"]을
value로 설정한다.
"stringMinimum"]을
value로 설정한다.
"stringMaximum"]을
value로 설정한다.
item, localState, globalState, usages, stringIndices로 HID 리포트 아이템을 생성하려면 다음 단계를 수행한다:
HIDReportItem
딕셔너리로 둔다.
unsigned long으로 해석한 값으로 둔다.
boolean 값들의
리스트로
둔다.
isConstant"]를
bitfield[0]으로 설정한다.
isArray"]를
!bitfield[1]로 설정한다.
isAbsolute"]를
!bitfield[2]로 설정한다.
wrap"]을
bitfield[3]으로 설정한다.
isLinear"]를
!bitfield[4]로 설정한다.
hasPreferredState"]를
bitfield[5]로 설정한다.
hasNull"]을
bitfield[6]으로 설정한다.
isVolatile"]을
bitfield[7]로 설정한다.
isBufferedBytes"]를
bitfield[8]로 설정한다.
usages"]를
usages로 설정한다.
reportSize"]를
globalState["reportSize"]로 설정한다.
reportCount"]를
globalState["reportCount"]로
설정한다.
unitExponent"]를
globalState["unitExponent"]로
설정한다.
unitSystem"]을
globalState["unitSystem"]로 설정한다.
unitFactorLengthExponent"]을
globalState["unitFactorLengthExponent"]로
설정한다.
unitFactorMassExponent"]를
globalState["unitFactorMassExponent"]로
설정한다.
unitFactorTimeExponent"]를
globalState["unitFactorTimeExponent"]로
설정한다.
unitFactorTemperatureExponent"]를
globalState["unitFactorTemperatureExponent"]로
설정한다.
unitFactorCurrentExponent"]를
globalState["unitFactorCurrentExponent"]로
설정한다.
unitFactorLuminousIntensityExponent"]를
globalState["unitFactorLuminousIntensityExponent"]로
설정한다.
logicalMinimum"]을
globalState["logicalMinimum"]로 설정한다.
logicalMaximum"]을
globalState["logicalMaximum"]로 설정한다.
"usageMinimum"이
localState에 있으면
reportItem["usageMinimum"]을
localState["usageMinimum"]로 설정한다.
"usageMaximum"이
localState에 있으면
reportItem["usageMaximum"]을
localState["usageMaximum"]로 설정한다.
isRange"]를
reportItem["usageMinimum"]이
reportItem["usageMaximum"]보다 작은 경우
true로, 그렇지 않으면 false로 설정한다.
"stringMinimum"과 "stringMaximum"이
localState에 모두 있으면:
시스템에서 HID 인터페이스가 사용 불가능해지면 다음 단계를 수행한다:
[[devices]]에서
해당 인터페이스를 나타내는
HIDDevice로 둔다.
Window
객체라면
그 객체의 hid getter를 호출한 결과로 하고,
그렇지 않으면 WorkerNavigator
객체의
hid getter를 호출한 결과로 한다.
[[devices]]에서.
requestDevice() 호출 결과로
사용자가 사이트가 device에 접근하는 것을 허용했다면,
hid의
관련 전역 객체에서
HID device task source를 사용해
글로벌 태스크를 큐잉하고,
이벤트를 발행한다.
이벤트 이름은 disconnect이며 대상은 hid이다.
이때 HIDConnectionEvent를 사용하고,
그 device 속성은
device로 초기화한다.
getDevices() 메서드의 단계는 다음과 같습니다:
null로 둡니다.DedicatedWorkerGlobalScope
이거나 window
객체이면, document를 this의
관련 글로벌
객체의
연결된
Document로 설정합니다.
ServiceWorkerGlobalScope
객체이고, 연결된 service worker
client가
window client라면, document를
연결된 service worker
client의
global object에
연결된 associated
Document로 설정합니다.
null이거나 document가
이름이 "hid"인 policy-controlled
feature를 allowed to
use가 아니면,
reject
promise를 "SecurityError" DOMException으로 하고
promise를 반환합니다.
HIDDevice)로 둡니다.
[[devices]]:
requestDevice()
호출 결과로 사이트가 device에 접근하도록 허용했고,
device.[[state]]가
"forgotten"이 아니면,
append device를 devices에 추가합니다.
requestDevice()
권한 대화상자는 연결된 장치 목록을 표시하고 사용자에게 하나를 선택하도록 요청합니다.
어떤 장치에 여러 개의 HID interfaces가 있는 경우, 대화상자는 모든 인터페이스를 대표하는 단일 항목만 MAY 표시할 수 있습니다. 사용자가 여러 HID interfaces를 대표하는 항목을 선택하면, 사용자 에이전트는 해당 장치가 노출하는 모든 HID interfaces에 대한 접근을 MUST 허용해야 합니다.
requestDevice() 메서드의 단계는 다음과
같습니다:
"hid"인 policy-controlled
feature를 allowed to
use가 아니면,
reject promise를
"SecurityError" DOMException으로 하고
promise를 반환합니다.
window가
아니면,
reject promise를
"NotSupportedError"
DOMException으로 하고
promise를 반환합니다.
SecurityError" DOMException으로 하고
promise를 반환합니다.
filters"]가 존재하면,
다음 단계를 for each
filter of
options["filters"]에 대해 실행합니다:
TypeError로
하고
promise를 반환합니다.
exclusionFilters"]가
존재하면, 다음 단계를 실행합니다:
exclusionFilters"]가
비어 있으면, reject promise를 TypeError로
하고
promise를 반환합니다.
exclusionFilters"]:
TypeError로
하고
promise를 반환합니다.
[[devices]]:
filters"]의
matches any filter이고,
device가
options["exclusionFilters"]의
어떤 필터와도 match any filter가
아니면,
append device를 availableDevices에 추가합니다.
HIDDevice)로 둡니다.
[[devices]]:
[[state]]를
"closed"로 설정하고,
append device를 devices에 추가합니다.
WebIDLdictionary HIDDeviceRequestOptions {
required sequence<HIDDeviceFilter> filters;
sequence<HIDDeviceFilter> exclusionFilters;
};
filters 멤버
exclusionFilters 멤버
WebIDLdictionary HIDDeviceFilter {
unsigned long vendorId;
unsigned short productId;
unsigned short usagePage;
unsigned short usage;
};
vendorId
멤버
productId
멤버
vendorId
멤버가 없으면 productId는 무시됩니다.
usagePage
멤버
usage 멤버
usagePage 멤버가 없으면
usage는 무시됩니다.
장치 필터는
requestDevice()가 생성하는
선택 대화상자에서
사용자에게 표시되는 장치 목록을 좁히는 데 사용됩니다. 필터가 제공되지 않으면 제외 필터에 일치하지 않는 모든 연결된 장치가 포함됩니다.
하나 이상의 필터가 제공되면, 임의의 필터에 일치하고 어떤 제외 필터에도 일치하지 않는 장치가 포함됩니다.
모든 HIDDeviceFilter
멤버는 선택 사항입니다. 각 멤버는 하나의 규칙을 지정합니다.
어떤 HIDDeviceFilter와
일치하려면 모든 규칙이 충족되어야 합니다.
비어 있는 HIDDeviceFilter는
모든 장치와 일치합니다.
장치 ID 규칙은 vendor ID와
product ID로 장치를 선택하는 데 사용됩니다.
이는 애플리케이션이 특정 장치에서만 동작하거나 벤더 전용 기능에 의존하는 경우 유용합니다.
vendorId 규칙을 포함하면
지정된 vendor ID를 가진 장치에만 일치합니다.
productId 규칙도 포함되면,
지정된 vendor ID와
product ID를 모두 가진 장치에만 일치합니다.
productId는
vendorId 규칙 없이 지정되면 무시됩니다.
사용 규칙은 장치의 HID usages가
할당된 top-level collections로
장치를 선택하는 데 사용됩니다. 이는 표준 장치 클래스와 동작하도록 설계된 애플리케이션에서 유용합니다.
장치와 일치하려면 장치의 컬렉션 중 적어도 하나가 모든 사용 규칙과 일치해야 합니다.
usagePage 규칙을 포함하면,
장치에 지정된 usage page를 가진 컬렉션이 있는 경우에만 일치합니다.
usage 규칙도 포함되면,
지정된 usage page와 usage ID를 모두 가진
장치에만 일치합니다.
usage는
usagePage 규칙 없이 지정되면
무시됩니다.
장치 ID 규칙과 사용 규칙은 함께 사용할 수 있습니다. 예를 들어, 특정 벤더의 장치 중에서도 특정 장치 클래스를 구현한 장치만 일치시키고자 할 수 있습니다.
exclusionFilters
옵션을 사용하면, 특정 장치 클래스를 구현했음에도 예상대로 동작하지 않는 것으로 알려진 장치를
requestDevice()가 생성하는
선택 대화상자의 목록에서 제외할 수 있습니다.
HIDDeviceFilter filter가 a valid
filter인지 여부는 다음 단계가 true를 반환하는지로 결정됩니다:
false를 반환합니다.
productId"]는 존재하는데
filter["vendorId"]가 존재하지 않으면,
false를 반환합니다.
usage"]는 존재하는데
filter["usagePage"]가 존재하지 않으면,
false를 반환합니다.
true를 반환합니다.
HIDDevice device가
sequence
filters의 HIDDeviceFilter에 대해
matches any filter인지 여부는 다음 단계가
true를 반환하는지로 결정됩니다:
true를 반환합니다.
true를 반환합니다.
false를 반환합니다.
HIDDeviceFilter filter가 device의
matches
the
device IDs인지 여부는 다음 단계가 true를 반환하는지로 결정됩니다:
vendorId"가
filter에 있으면:
true를 반환합니다.
HIDDeviceFilter filter가 device의
matches
any
collection인지 여부는 다음 단계가 true를 반환하는지로 결정됩니다:
collections:
true를 반환합니다.
false를 반환합니다.
HIDDeviceFilter filter가 collection에
대해
matches
the
collection인지 여부는 다음 단계가 true를 반환하는지로 결정됩니다:
WebIDL[Exposed=(DedicatedWorker,ServiceWorker,Window), SecureContext]
interface HIDDevice : EventTarget {
attribute EventHandler oninputreport;
readonly attribute boolean opened;
readonly attribute unsigned short vendorId;
readonly attribute unsigned short productId;
readonly attribute DOMString productName;
readonly attribute FrozenArray<HIDCollectionInfo> collections;
Promise<undefined> open();
Promise<undefined> close();
Promise<undefined> forget();
Promise<undefined> sendReport([EnforceRange] octet reportId, BufferSource data);
Promise<undefined> sendFeatureReport(
[EnforceRange] octet reportId,
BufferSource data);
Promise<DataView> receiveFeatureReport([EnforceRange] octet reportId);
};
이 인터페이스의 메서드들은 일반적으로 비동기적으로 완료되며, HID device task source에 작업을 큐잉합니다.
HIDDevice의 인스턴스는 다음 표에 설명된 내부 슬롯과 함께 생성됩니다:
| 내부 슬롯 | 초기값 | 설명(비규범적) |
|---|---|---|
| [[state]] |
"closed"
|
HIDDevice의 활성 상태를 추적합니다
|
| [[vendorId]] | 0 | 장치의 vendor ID |
| [[productId]] | 0 | 장치의 product ID |
| [[productName]] |
""
|
장치의 product name |
| [[collections]] |
HIDCollectionInfo의
빈 sequence
|
report descriptor 파싱으로 생성된 top-level collections |
| [[pendingSendReportPromises]] | 빈 list |
보류 중인 sendReport()
Promise
|
| [[pendingSendFeatureReportPromises]] | 빈 list |
보류 중인 sendFeatureReport()
Promise
|
| [[pendingReceiveFeatureReportPromises]] | 빈 list |
보류 중인 receiveFeatureReport()
Promise
|
oninputreport 속성
oninputreport는
이벤트 핸들러
IDL 속성으로,
inputreport
이벤트 유형에 해당합니다.
opened
속성
HIDDevice가 데이터 전송을 할 준비가 되었음을 나타내는 플래그입니다.
opened getter 단계는 다음과 같습니다:
vendorId
속성
vendor ID는
장치 제조사를 식별하기 위해 vendor ID source가 할당하는 unsigned short 값입니다.
vendorId 속성은 장치의
vendor ID입니다.
장치에 vendor ID가 없거나
vendor ID에 접근할 수 없으면,
이 속성은 MUST 0이어야 합니다.
vendorId getter 단계는 다음과 같습니다:
[[vendorId]]를 반환합니다.
productId
속성
product ID는
제조사가 제품을 식별하기 위해 할당하는 unsigned short 값입니다.
productId 속성은 장치의
product ID입니다.
장치에 product ID가 없거나
product ID에 접근할 수 없으면,
이 속성은 MUST 0이어야 합니다.
productId getter 단계는 다음과 같습니다:
[[productId]]를 반환합니다.
productName 속성
제품을 식별하는 문자열입니다.
USB HID 장치의 경우, product name에는 장치 서술자(Device Descriptor)에 지정된
인덱스 iProduct의 문자열 서술자 값이 SHOULD 포함되어야 합니다.
Bluetooth HID 장치의 경우, product name에는
Device Name 특성의 값이 SHOULD 포함되어야 합니다.
장치에 product name이 없거나
product name이 비어 있거나 접근할 수 없으면,
이 속성은 MUST 빈 문자열을 반환해야 합니다.
productName 문자열에는
일련 번호나 Bluetooth 장치 주소와 같이 장치를 고유하게 식별할 수 있는 식별자가
SHOULD NOT 포함되어야 합니다.
productName getter 단계는 다음과 같습니다:
[[productName]]를 반환합니다.
collections 속성
sequence의
HIDCollectionInfo로,
report descriptor의
top-level collections을 나타냅니다.
collections getter 단계는 다음과 같습니다:
[[collections]]를 반환합니다.
device로부터 입력 리포트를 수신하면, 다음 단계를 수행합니다:
DataView를
data로 둡니다.
HID interface가
uses report IDs를 사용하는 경우,
이 byte sequence에는
리포트 ID 바이트가 MUST NOT 포함되면 안 됩니다.
inputreport라는 이름의 이벤트를
device에서 발행합니다. 이때
HIDInputReportEvent를
사용하고,
그 device 속성은 device로,
reportId 속성은 reportId로,
data 속성은 data로 초기화합니다.
open() 메서드의 단계는 다음과 같습니다:
[[state]]가 "closed"가 아니면,
reject promise를
"InvalidStateError"
DOMException으로 하고
promise를 반환합니다.
[[state]]를 "opening"으로 설정합니다.
NetworkError"
DOMException으로
reject promise하고,
이후 단계를 중단합니다.
[[state]]를 "opened"로 설정합니다.
undefined로 설정합니다.
close() 메서드의 단계는 다음과 같습니다:
[[state]]가 "forgotten" 또는
"forgetting"이면,
"InvalidStateError"
DOMException으로
reject
promise하고 promise를 반환합니다.
[[state]]를 "closing"으로 설정합니다.
[[pendingSendReportPromises]]:
AbortError"
DOMException으로
거부합니다.
[[pendingSendReportPromises]].
[[pendingSendFeatureReportPromises]]:
AbortError"
DOMException으로
거부합니다.
[[pendingSendFeatureReportPromises]].
[[pendingReceiveFeatureReportPromises]]:
AbortError"
DOMException으로
거부합니다.
[[pendingReceiveFeatureReportPromises]].
[[state]]를 "closed"로 설정합니다.
undefined로 설정합니다.
사용자 에이전트는 API 전반의 권한을 결합하기로 MAY 선택할 수 있습니다. 예를 들어, WebHID + WebUSB 장치 접근을 통합된 저수준 장치 접근 권한으로 추적할 수 있습니다. 이러한 이유로 이 메서드는 향후 추가적인(아직 지정되지 않은) 권한들도 철회할 수 있습니다.
forget() 메서드의 단계는 다음과 같습니다:
[[devices]]:
[[state]]를
"forgetting"으로 설정합니다.
[[pendingSendReportPromises]]:
AbortError"
DOMException으로
거부합니다.
[[pendingSendReportPromises]].
[[pendingSendFeatureReportPromises]]:
AbortError"
DOMException으로
거부합니다.
[[pendingSendFeatureReportPromises]].
[[pendingReceiveFeatureReportPromises]]:
AbortError"
DOMException으로
거부합니다.
[[pendingReceiveFeatureReportPromises]].
[[state]]를
"forgotten"으로 설정합니다.
undefined로 설정합니다.
sendReport() 메서드는
지정된 reportId와 data를 사용해
출력 리포트를 전송하는 데 사용됩니다.
HID interface가
use report IDs를 사용하지 않으면,
리포트 ID 대신 0을 전달하십시오.
HID interfaces 중 use report IDs를 사용하는 경우, 리포트 ID 값 0은 예약되어 있으며 사용하지 않아야 합니다.
sendReport() 메서드의 단계는 다음과
같습니다:
[[state]]가 "opened"가 아니면,
"InvalidStateError"
DOMException으로
reject
promise하고 promise를 반환합니다.
TypeError"
DOMException으로
reject
promise하고 promise를 반환합니다.
[[pendingSendReportPromises]]에
추가합니다.
NotAllowedError"
DOMException으로
reject
promise하고 이후 단계를 중단합니다.
NetworkError"
DOMException으로
reject
promise하고 이후 단계를 중단합니다.
undefined로 설정합니다.
sendFeatureReport()
메서드는 지정된 reportId와 data로
feature 리포트를 전송하는 데 사용됩니다.
HID interface가
use report IDs를 사용하지 않으면,
리포트 ID 대신 0을 전달하십시오.
HID interfaces 중 use report IDs를 사용하는 경우, 리포트 ID 값 0은 예약되어 있으며 사용하지 않아야 합니다.
sendFeatureReport()
메서드의 단계는 다음과 같습니다:
[[state]]가 "opened"가 아니면,
"InvalidStateError"
DOMException으로
reject
promise하고 promise를 반환합니다.
TypeError"
DOMException으로
reject
promise하고 promise를 반환합니다.
[[pendingSendFeatureReportPromises]]에
추가합니다.
NotAllowedError"
DOMException으로
reject
promise하고 이후 단계를 중단합니다.
NetworkError"
DOMException으로
reject
promise하고 이후 단계를 중단합니다.
undefined로 설정합니다.
receiveFeatureReport()
메서드는 지정된 reportId의 feature 리포트를 요청하는 데 사용됩니다.
HID interface가
use report IDs를 사용하지 않으면,
리포트 ID 대신 0을 전달하십시오.
HID interfaces 중 use report IDs를 사용하는 경우, 리포트 ID 값 0은 예약되어 있으며 사용하지 않아야 합니다.
receiveFeatureReport()
메서드의 단계는 다음과 같습니다:
[[state]]가 "opened"가 아니면,
"InvalidStateError"
DOMException으로
reject
promise하고 promise를 반환합니다.
TypeError"
DOMException으로
reject
promise하고 promise를 반환합니다.
[[pendingReceiveFeatureReportPromises]]에
추가합니다.
NotAllowedError"
DOMException으로
reject
promise하고 이후 단계를 중단합니다.
DataView.
NetworkError"
DOMException으로
reject
promise하고 이후 단계를 중단합니다.
WebIDL[Exposed=(DedicatedWorker,ServiceWorker,Window), SecureContext]
interface HIDConnectionEvent : Event {
constructor(DOMString type, HIDConnectionEventInit eventInitDict);
[SameObject] readonly attribute HIDDevice device;
};
device 속성
HIDDevice 인스턴스입니다.
WebIDLdictionary HIDConnectionEventInit : EventInit {
required HIDDevice device;
};
device 멤버
WebIDL[Exposed=(DedicatedWorker,ServiceWorker,Window), SecureContext]
interface HIDInputReportEvent : Event {
constructor(DOMString type, HIDInputReportEventInit eventInitDict);
[SameObject] readonly attribute HIDDevice device;
readonly attribute octet reportId;
readonly attribute DataView data;
};
device 속성
HIDDevice
인스턴스입니다.
reportId 속성
data
속성
DataView입니다. HID 인터페이스가
report ID를 사용한다면 reportId 바이트는 제외됩니다.
WebIDLdictionary HIDInputReportEventInit : EventInit {
required HIDDevice device;
required octet reportId;
required DataView data;
};
device 멤버
reportId 멤버
data 멤버
WebIDLdictionary HIDCollectionInfo {
unsigned short usagePage;
unsigned short usage;
octet type;
sequence<HIDCollectionInfo> children;
sequence<HIDReportInfo> inputReports;
sequence<HIDReportInfo> outputReports;
sequence<HIDReportInfo> featureReports;
};
장치가 시스템에 처음 연결되면, 시스템은 장치가 노출하는 각 HID 인터페이스에 대한 리포트 디스크립터를 MUST 가져와야 합니다. Report descriptor는 각 리포트의 레이아웃을 설명하는 계층적 데이터 구조로 확장될 수 있는 바이트 시퀀스입니다. 이 데이터 구조의 요소를 항목(items)이라 하며, 관계를 공유하는 항목들의 그룹을 컬렉션(collections)이라 합니다.
HIDCollectionInfo 객체는
리포트 디스크립터 내의 단일 컬렉션을 나타냅니다.
컬렉션이 중첩된 컬렉션을 포함하는 경우, 중첩된 컬렉션은
children의 요소로 포함됩니다.
inputReports,
outputReports,
featureReports 속성에는
이 컬렉션에 설명된 각 리포트에 대한 정보를 제공하는
시퀀스의
HIDReportInfo가 포함됩니다.
최상위(top-level) 컬렉션의 경우,
HIDReportInfo는 리포트를 구성하는 모든 항목의 평탄화된 뷰를 나타냅니다.
평탄화된 뷰는 중첩된 컬렉션 내 항목과
최상위 컬렉션 내 항목을 교차 배치합니다.
중첩 컬렉션의 경우 HIDReportInfo에는
해당 컬렉션과 그 중첩 컬렉션에 포함된 항목만 포함됩니다.
usagePage 멤버
이 컬렉션과 연관된 HID usage의 usage page 구성요소입니다.
HID usages는 애플리케이션이 항목이나 컬렉션의 목적과 의미를
식별하는 데 사용할 수 있는 상수입니다.
HID usage는 상위 비트의 unsigned short
usage page와
하위 비트의 unsigned short
usage ID로 구성된
unsigned long 값입니다.
표준 HID usage 값은
[USBIF-HID-CLASS]
및 USB Implementers Forum이 발행한 다른 문서에 설명되어 있습니다.
usage 멤버
이 컬렉션과 연관된 HID usage의 usage ID 구성요소입니다.
usage ID는 usage page에서 개별 HID usage를 선택하는 데 사용됩니다. 관례적으로 usage ID 0x01~0x1F는 최상위 컬렉션을 위해 예약되어 있습니다.
type 멤버
컬렉션 타입을 나타내는 값입니다.
| 컬렉션 타입 | 값 |
|---|---|
| 물리(Physical) | 0x00 |
| 애플리케이션(Application) | 0x01 |
| 논리(Logical) | 0x02 |
| 리포트(Report) | 0x03 |
| 명명된 배열(Named Array) | 0x04 |
| 사용 전환(Usage Switch) | 0x05 |
| 사용 수정됨(Usage Modified) | 0x06 |
| 향후 사용을 위해 예약(Reserved for future use) | 0x07 to 0x7F |
| 벤더 정의(Vendor-defined) | 0x80 to 0xFF |
각 컬렉션 타입은 그룹화된 항목 간의 서로 다른 관계를 설명합니다. USBIF-HID-CLASS의 6.2.2.6 절을 참고하여 컬렉션 타입에 대해 더 알아볼 수 있습니다.
children 멤버
HIDCollectionInfo입니다.
inputReports 멤버
HIDReportInfo입니다.
outputReports 멤버
HIDReportInfo입니다.
featureReports 멤버
HIDReportInfo입니다.
WebIDLdictionary HIDReportInfo {
octet reportId;
sequence<HIDReportItem> items;
};
HIDReportInfo는
리포트 디스크립터 내의 하나의 입력, 출력 또는 기능 리포트를 나타냅니다.
reportId 멤버
reportId 멤버는 이 리포트의 접두사이며,
그렇지 않으면 0입니다.
items 멤버
HIDReportItem입니다.
WebIDLdictionary HIDReportItem {
boolean isAbsolute;
boolean isArray;
boolean isBufferedBytes;
boolean isConstant;
boolean isLinear;
boolean isRange;
boolean isVolatile;
boolean hasNull;
boolean hasPreferredState;
boolean wrap;
sequence<unsigned long> usages;
unsigned long usageMinimum;
unsigned long usageMaximum;
unsigned short reportSize;
unsigned short reportCount;
byte unitExponent;
HIDUnitSystem unitSystem;
byte unitFactorLengthExponent;
byte unitFactorMassExponent;
byte unitFactorTimeExponent;
byte unitFactorTemperatureExponent;
byte unitFactorCurrentExponent;
byte unitFactorLuminousIntensityExponent;
long logicalMinimum;
long logicalMaximum;
long physicalMinimum;
long physicalMaximum;
sequence<DOMString> strings;
};
isAbsolute 멤버
true, 마지막 리포트로부터의 값 변화를 나타내는 상대값이면 false입니다.
isArray 멤버
true,
각 데이터 필드가 값을 포함하는 변수 데이터 필드를 리포트에 생성하면 false입니다.
isBufferedBytes 멤버
true, 항목이 수치형 값이면 false입니다.
isConstant 멤버
true, 수정 가능한 장치 데이터를 포함하는 리포트 필드를 정의하면 false입니다.
isLinear 멤버
true, 데이터가 어떤 방식으로든 처리되었으면 false입니다.
isRange 멤버
usageMinimum과
usageMaximum으로 정의된 사용값을 할당하면
true,
항목에 시퀀스의
HID usage 값이
usages에 있으면 false입니다.
isVolatile 멤버
호스트의 상호작용 없이 항목 값이 변경될 수 있으면 true, 항목 값이 오직 호스트에 의해서만 변경되어야 하면 false입니다.
기능(Feature) 및 출력(Output) 항목에만 사용됩니다.
hasNull 멤버
true, 없으면 false입니다.
널 상태에서는 항목이 지정된
logicalMinimum 및
logicalMaximum 범위를 벗어난 값을 보고합니다.
hasPreferredState 멤버
true, 없으면 false입니다.
wrap 멤버
true, 롤오버되지 않으면 false입니다.
usages 멤버
isRange가 true이거나
이 항목에 연관된 HID usage 값이 없다면,
usages는 MUST
미정의(undefined)여야 합니다.
usageMinimum 멤버
isRange가 true이면
usageMinimum에는
이 항목과 연관된 사용 범위의 최소
HID usage 값이 포함됩니다.
isRange가 false이면
usageMinimum은
MUST 미정의(undefined)여야 합니다.
usageMaximum 멤버
isRange가 true이면
usageMaximum에는
이 항목과 연관된 사용 범위의 최대
HID usage 값이 포함됩니다.
isRange가 false이면
usageMaximum은
MUST 미정의(undefined)여야 합니다.
reportSize 멤버
reportSize는
MUST 0보다 커야 합니다.
reportCount 멤버
reportCount는
MUST 0보다 커야 합니다.
unitExponent 멤버
unitSystem 멤버
HIDUnitSystem 열거값이며,
항목에 단위 정의가 없으면 "none"입니다.
unitFactorLengthExponent 멤버
unitFactorMassExponent 멤버
unitFactorTimeExponent 멤버
unitFactorTemperatureExponent 멤버
unitFactorCurrentExponent
멤버
unitFactorLuminousIntensityExponent 멤버
logicalMinimum 멤버
logicalMaximum 멤버
physicalMinimum 멤버
logicalMinimum을 의미합니다.
physicalMaximum 멤버
logicalMaximum을 의미합니다.
strings 멤버
[USBIF-HID-CLASS] 6.2.2.7 절은 네 가지 표준 단위계를 정의합니다: SI 선형, SI 회전, 영국식 선형, 영국식 회전. 각 항목은 이 네 가지 단위계 중 하나, 벤더 정의 단위계, 또는 단위계 없음 중 하나를 사용합니다.
WebIDLenum HIDUnitSystem {
"none", "si-linear", "si-rotation", "english-linear",
"english-rotation", "vendor-defined", "reserved"
};
"none"
"si-linear"
"si-rotation"
"english-linear"
"english-rotation"
"vendor-defined"
"reserved"
이 명세는 웹사이트가 접근할 수 있는 HID 장치 집합을 제한하기 위해 https://github.com/WICG/webhid 리포지토리의 차단 목록 파일에 의존합니다.
url에서 차단 목록을 파싱한 결과는 시퀀스의 딕셔너리 객체로, url을 가져와서 그 내용을 JSON으로 파싱함으로써 생성됩니다.
HID 차단 목록은 차단 목록을 파싱한 결과이며, 위치는 https://github.com/WICG/webhid/blob/main/blocklist.txt 입니다.
다음 단계가 true를 반환하면, 리포트는
차단된 리포트입니다:
true를 반환합니다.
false를 반환합니다.
다음 단계가 true를 반환하면, 리포트는 rule에 의해
차단 목록 규칙에
의해 차단됩니다:
"input"으로,
출력 리포트이면 "output"으로, 기능 리포트이면 "feature"로 둡니다.
"vendor"가 포함되어 있고 rule["vendor"]가
device.vendorId와 다르면 false를 반환합니다.
"product"가 포함되어 있고 rule["product"]가
device.productId와 다르면 false를 반환합니다.
"reportId"가 포함되어 있고 rule["reportId"]가
reportId와 다르면 false를 반환합니다.
"reportType"가 포함되어 있고 rule["reportType"]가
reportType과 다르면 false를 반환합니다.
"usagePage"가 포함되어 있고 rule["usagePage"]가
collection["usagePage"]와 다르면
false를 반환합니다.
"usage"가 포함되어 있고 rule.["usage"]가
collection["usage"]와 다르면 false를 반환합니다.
true를 반환합니다.
이 명세는 hid 속성이
Navigator
객체에 노출되는지를 제어하는 기능을 정의합니다.
이 기능의 기능 이름은 "hid"입니다.
이 기능의 기본 허용
목록은
'self'입니다.
비규범적(non-normative)으로 표시된 절뿐만 아니라, 이 명세의 모든 작성 지침, 다이어그램, 예제, 주석은 비규범적입니다. 그 외 이 명세의 모든 내용은 규범적입니다.
이 문서의 MAY, MUST, MUST NOT, SHOULD, SHOULD NOT 키워드는 BCP 14 [RFC2119] [RFC8174] 에서 설명된 대로, 그리고 오직 여기에서처럼 모두 대문자로 나타날 때에만 해석되어야 합니다.
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: