mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-20 23:37:33 +09:00
WebUSB initial commit
This commit is contained in:
parent
c461f87cc7
commit
b898673be8
22 changed files with 2231 additions and 1 deletions
|
|
@ -2241,6 +2241,9 @@ const ContentPermissionIntegration = {
|
|||
case "geolocation": {
|
||||
return new PermissionUI.GeolocationPermissionPrompt(request);
|
||||
}
|
||||
case "usb": {
|
||||
return new PermissionUI.USBPermissionPrompt(request);
|
||||
}
|
||||
case "desktop-notification": {
|
||||
return new PermissionUI.DesktopNotificationPermissionPrompt(request);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@
|
|||
<preference id="javascript.options.wasm"
|
||||
name="javascript.options.wasm"
|
||||
type="bool"/>
|
||||
<preference id="dom.usb.enabled"
|
||||
name="dom.usb.enabled"
|
||||
type="bool"/>
|
||||
#ifdef MOZ_WEBRTC
|
||||
<preference id="media.peerconnection.enabled"
|
||||
name="media.peerconnection.enabled"
|
||||
|
|
@ -246,6 +249,14 @@
|
|||
accesskey="&javascriptWasm.accesskey;" />
|
||||
</vbox>
|
||||
</row>
|
||||
<row id="javascriptWebUSBRow">
|
||||
<vbox align="start">
|
||||
<checkbox id="javascriptWebUSBPolicy"
|
||||
preference="dom.usb.enabled"
|
||||
label="&javascriptWebusb.label;"
|
||||
accesskey="&javascriptWebusb.accesskey;" />
|
||||
</vbox>
|
||||
</row>
|
||||
#ifdef MOZ_WEBRTC
|
||||
<row id="javascriptRow2">
|
||||
<vbox align="start">
|
||||
|
|
|
|||
|
|
@ -367,6 +367,12 @@ geolocation.neverShareLocation.accesskey=N
|
|||
geolocation.shareWithSite2=Would you like to share your location with this site?
|
||||
geolocation.shareWithFile2=Would you like to share your location with this file?
|
||||
|
||||
webUSB.connectToSite=Would you like to let this site access USB devices?
|
||||
webUSB.allow=Allow USB Devices
|
||||
webUSB.allow.accesskey=A
|
||||
webUSB.block=Block USB Devices
|
||||
webUSB.block.accesskey=B
|
||||
|
||||
webNotifications.receiveForSession=Receive for this session
|
||||
webNotifications.receiveForSession.accesskey=s
|
||||
webNotifications.alwaysReceive=Always Receive Notifications
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@
|
|||
|
||||
<!ENTITY javascriptWasm.label "Enable WebAssembly (WASM)">
|
||||
<!ENTITY javascriptWasm.accesskey "y">
|
||||
<!ENTITY javascriptWebusb.label "Enable WebUSB">
|
||||
<!ENTITY javascriptWebusb.accesskey "U">
|
||||
#ifdef MOZ_WEBRTC
|
||||
<!ENTITY javascriptWebrtc.label "Enable WebRTC">
|
||||
<!ENTITY javascriptWebrtc.accesskey "R">
|
||||
|
|
|
|||
|
|
@ -472,6 +472,59 @@ GeolocationPermissionPrompt.prototype = {
|
|||
|
||||
PermissionUI.GeolocationPermissionPrompt = GeolocationPermissionPrompt;
|
||||
|
||||
/**
|
||||
* Creates a PermissionPrompt for the WebUSB API.
|
||||
* USB access is granted for the current browser session.
|
||||
*/
|
||||
function getUSBString(name, fallback) {
|
||||
try {
|
||||
return gBrowserBundle.GetStringFromName(name);
|
||||
} catch (ex) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
function USBPermissionPrompt(request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
USBPermissionPrompt.prototype = {
|
||||
__proto__: PermissionPromptForRequestPrototype,
|
||||
|
||||
get permissionKey() {
|
||||
return "usb";
|
||||
},
|
||||
|
||||
get notificationID() {
|
||||
return "web-usb";
|
||||
},
|
||||
|
||||
get anchorID() {
|
||||
return "default-notification-icon";
|
||||
},
|
||||
|
||||
get message() {
|
||||
return getUSBString("webUSB.connectToSite",
|
||||
"Would you like to let this site access USB devices?");
|
||||
},
|
||||
|
||||
get promptActions() {
|
||||
return [{
|
||||
label: getUSBString("webUSB.allow", "Allow USB Devices"),
|
||||
accessKey: getUSBString("webUSB.allow.accesskey", "A"),
|
||||
action: Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION,
|
||||
}, {
|
||||
label: getUSBString("webUSB.block", "Block USB Devices"),
|
||||
accessKey: getUSBString("webUSB.block.accesskey", "B"),
|
||||
action: Ci.nsIPermissionManager.DENY_ACTION,
|
||||
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION,
|
||||
}];
|
||||
},
|
||||
};
|
||||
|
||||
PermissionUI.USBPermissionPrompt = USBPermissionPrompt;
|
||||
|
||||
/**
|
||||
* Creates a PermissionPrompt for a nsIContentPermissionRequest for
|
||||
* the Desktop Notification API.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "mozilla/dom/Permissions.h"
|
||||
#include "mozilla/dom/ServiceWorkerContainer.h"
|
||||
#include "mozilla/dom/StorageManager.h"
|
||||
#include "mozilla/dom/USB.h"
|
||||
#include "mozilla/dom/TCPSocket.h"
|
||||
#include "mozilla/dom/URLSearchParams.h"
|
||||
#include "mozilla/dom/workers/RuntimeService.h"
|
||||
|
|
@ -189,6 +190,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUSB)
|
||||
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelManager)
|
||||
#endif
|
||||
|
|
@ -224,6 +226,7 @@ Navigator::Invalidate()
|
|||
mPermissions = nullptr;
|
||||
|
||||
mStorageManager = nullptr;
|
||||
mUSB = nullptr;
|
||||
|
||||
// If there is a page transition, make sure delete the geolocation object.
|
||||
if (mGeolocation) {
|
||||
|
|
@ -547,6 +550,21 @@ Navigator::Storage()
|
|||
return mStorageManager;
|
||||
}
|
||||
|
||||
USB*
|
||||
Navigator::GetUsb()
|
||||
{
|
||||
MOZ_ASSERT(mWindow);
|
||||
|
||||
if (!mUSB) {
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
mUSB = new USB(global);
|
||||
}
|
||||
|
||||
return mUSB;
|
||||
}
|
||||
|
||||
// Values for the network.cookie.cookieBehavior pref are documented in
|
||||
// nsCookieService.cpp.
|
||||
#define COOKIE_BEHAVIOR_REJECT 2
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ class Connection;
|
|||
class PowerManager;
|
||||
class LegacyMozTCPSocket;
|
||||
class StorageManager;
|
||||
class USB;
|
||||
|
||||
namespace time {
|
||||
class TimeManager;
|
||||
|
|
@ -226,6 +227,7 @@ public:
|
|||
bool MozE10sEnabled();
|
||||
|
||||
StorageManager* Storage();
|
||||
USB* GetUsb();
|
||||
|
||||
static void GetAcceptLanguages(nsTArray<nsString>& aLanguages);
|
||||
|
||||
|
|
@ -298,6 +300,7 @@ private:
|
|||
nsTArray<RefPtr<Promise> > mVRGetDisplaysPromises;
|
||||
nsTArray<uint32_t> mRequestedVibrationPattern;
|
||||
RefPtr<StorageManager> mStorageManager;
|
||||
RefPtr<USB> mUSB;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ DIRS += [
|
|||
'xul',
|
||||
'manifest',
|
||||
'u2f',
|
||||
'usb',
|
||||
'console',
|
||||
'performance',
|
||||
'xhr',
|
||||
|
|
@ -113,4 +114,3 @@ TEST_DIRS += [
|
|||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'windows', 'android', 'cocoa'):
|
||||
TEST_DIRS += ['plugins/test']
|
||||
|
||||
|
|
|
|||
297
dom/usb/USB.cpp
Normal file
297
dom/usb/USB.cpp
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
#include "USB.h"
|
||||
|
||||
#include "mozilla/dom/USBBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/USBDevice.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsError.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
bool
|
||||
MatchesFilter(const USBDeviceInfo& aDevice, const USBDeviceFilter& aFilter)
|
||||
{
|
||||
if (aFilter.mVendorId.WasPassed() &&
|
||||
aFilter.mVendorId.Value() != aDevice.mVendorId) {
|
||||
return false;
|
||||
}
|
||||
if (aFilter.mProductId.WasPassed() &&
|
||||
aFilter.mProductId.Value() != aDevice.mProductId) {
|
||||
return false;
|
||||
}
|
||||
if (aFilter.mClassCode.WasPassed() &&
|
||||
aFilter.mClassCode.Value() != aDevice.mDeviceClass) {
|
||||
return false;
|
||||
}
|
||||
if (aFilter.mSubclassCode.WasPassed() &&
|
||||
aFilter.mSubclassCode.Value() != aDevice.mDeviceSubclass) {
|
||||
return false;
|
||||
}
|
||||
if (aFilter.mProtocolCode.WasPassed() &&
|
||||
aFilter.mProtocolCode.Value() != aDevice.mDeviceProtocol) {
|
||||
return false;
|
||||
}
|
||||
if (aFilter.mSerialNumber.WasPassed() &&
|
||||
!aFilter.mSerialNumber.Value().Equals(aDevice.mSerialNumber)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MatchesRequest(const USBDeviceInfo& aDevice,
|
||||
const USBDeviceRequestOptions& aOptions)
|
||||
{
|
||||
bool included = false;
|
||||
for (const auto& filter : aOptions.mFilters) {
|
||||
if (MatchesFilter(aDevice, filter)) {
|
||||
included = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!included) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& filter : aOptions.mExclusionFilters) {
|
||||
if (MatchesFilter(aDevice, filter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class USBPermissionRequest final : public nsIContentPermissionRequest
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPERMISSIONREQUEST
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(USBPermissionRequest,
|
||||
nsIContentPermissionRequest)
|
||||
|
||||
USBPermissionRequest(USB* aUSB,
|
||||
Promise* aPromise,
|
||||
nsPIDOMWindowInner* aWindow,
|
||||
nsTArray<RefPtr<USBDevice>>&& aCandidates)
|
||||
: mUSB(aUSB)
|
||||
, mPromise(aPromise)
|
||||
, mWindow(aWindow)
|
||||
, mCandidates(Move(aCandidates))
|
||||
, mRequester(new nsContentPermissionRequester(aWindow))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
~USBPermissionRequest() = default;
|
||||
|
||||
RefPtr<USB> mUSB;
|
||||
RefPtr<Promise> mPromise;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
nsTArray<RefPtr<USBDevice>> mCandidates;
|
||||
RefPtr<nsContentPermissionRequester> mRequester;
|
||||
};
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USBPermissionRequest)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(USBPermissionRequest)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(USBPermissionRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION(USBPermissionRequest, mUSB, mPromise, mWindow,
|
||||
mCandidates, mRequester)
|
||||
|
||||
NS_IMETHODIMP
|
||||
USBPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPrincipal);
|
||||
*aPrincipal = nullptr;
|
||||
if (!mWindow || !mWindow->GetDoc()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIPrincipal> principal = mWindow->GetDoc()->NodePrincipal();
|
||||
principal.forget(aPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
USBPermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
nsTArray<nsString> options;
|
||||
for (const auto& device : mCandidates) {
|
||||
Nullable<nsString> label;
|
||||
device->GetProductName(label);
|
||||
if (label.IsNull() || label.Value().IsEmpty()) {
|
||||
options.AppendElement(NS_LITERAL_STRING("USB device"));
|
||||
} else {
|
||||
options.AppendElement(label.Value());
|
||||
}
|
||||
}
|
||||
return nsContentPermissionUtils::CreatePermissionArray(
|
||||
NS_LITERAL_CSTRING("usb"), NS_LITERAL_CSTRING("device"), options, aTypes);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
USBPermissionRequest::GetWindow(mozIDOMWindow** aWindow)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aWindow);
|
||||
nsCOMPtr<mozIDOMWindow> window = do_QueryInterface(mWindow);
|
||||
window.forget(aWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
USBPermissionRequest::GetElement(nsIDOMElement** aElement)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aElement);
|
||||
*aElement = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
USBPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRequester);
|
||||
nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
|
||||
requester.forget(aRequester);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
USBPermissionRequest::Cancel()
|
||||
{
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
USBPermissionRequest::Allow(JS::HandleValue aChoices)
|
||||
{
|
||||
(void)aChoices;
|
||||
if (mCandidates.IsEmpty()) {
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<USBDevice> device = mCandidates[0];
|
||||
mUSB->AddAuthorizedDevice(device);
|
||||
mPromise->MaybeResolve(device);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(USB, DOMEventTargetHelper,
|
||||
mAuthorizedDevices)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USB)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(USB, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(USB, DOMEventTargetHelper)
|
||||
|
||||
USB::USB(nsIGlobalObject* aOwner)
|
||||
: DOMEventTargetHelper(aOwner)
|
||||
{
|
||||
}
|
||||
|
||||
USB::~USB()
|
||||
{
|
||||
for (const auto& device : mAuthorizedDevices) {
|
||||
device->ClearUSB();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
USB::AddAuthorizedDevice(USBDevice* aDevice)
|
||||
{
|
||||
if (!aDevice || mAuthorizedDevices.Contains(aDevice)) {
|
||||
return;
|
||||
}
|
||||
aDevice->SetUSB(this);
|
||||
mAuthorizedDevices.AppendElement(aDevice);
|
||||
}
|
||||
|
||||
void
|
||||
USB::RemoveAuthorizedDevice(USBDevice* aDevice)
|
||||
{
|
||||
if (!aDevice) {
|
||||
return;
|
||||
}
|
||||
aDevice->ClearUSB();
|
||||
mAuthorizedDevices.RemoveElement(aDevice);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
USB::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return USBBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USB::GetDevices(ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
promise->MaybeResolve(mAuthorizedDevices);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USB::RequestDevice(const USBDeviceRequestOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsTArray<USBDeviceInfo> infos;
|
||||
nsresult rv = EnumerateUSBDevices(infos);
|
||||
if (NS_FAILED(rv)) {
|
||||
promise->MaybeReject(rv);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<USBDevice>> candidates;
|
||||
for (const auto& info : infos) {
|
||||
if (!MatchesRequest(info, aOptions)) {
|
||||
continue;
|
||||
}
|
||||
RefPtr<USBDevice> device = new USBDevice(GetOwnerGlobal());
|
||||
device->SetDeviceInfo(info);
|
||||
candidates.AppendElement(device);
|
||||
}
|
||||
|
||||
if (candidates.IsEmpty()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetOwnerGlobal());
|
||||
if (!window) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
RefPtr<USBPermissionRequest> request =
|
||||
new USBPermissionRequest(this, promise, window, Move(candidates));
|
||||
rv = nsContentPermissionUtils::AskPermission(request, window);
|
||||
if (NS_FAILED(rv)) {
|
||||
promise->MaybeReject(rv);
|
||||
}
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
48
dom/usb/USB.h
Normal file
48
dom/usb/USB.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
#ifndef mozilla_dom_USB_h
|
||||
#define mozilla_dom_USB_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/USBBackend.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class USBDevice;
|
||||
struct USBDeviceRequestOptions;
|
||||
|
||||
class USB final : public DOMEventTargetHelper
|
||||
{
|
||||
public:
|
||||
explicit USB(nsIGlobalObject* aOwner);
|
||||
|
||||
IMPL_EVENT_HANDLER(connect)
|
||||
IMPL_EVENT_HANDLER(disconnect)
|
||||
|
||||
already_AddRefed<Promise> GetDevices(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> RequestDevice(
|
||||
const USBDeviceRequestOptions& aOptions, ErrorResult& aRv);
|
||||
|
||||
void AddAuthorizedDevice(USBDevice* aDevice);
|
||||
void RemoveAuthorizedDevice(USBDevice* aDevice);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(USB, DOMEventTargetHelper)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
~USB();
|
||||
|
||||
nsTArray<RefPtr<USBDevice>> mAuthorizedDevices;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_USB_h
|
||||
27
dom/usb/USBBackend.cpp
Normal file
27
dom/usb/USBBackend.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
#include "USBBackend.h"
|
||||
|
||||
#include "nsError.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#ifndef XP_WIN
|
||||
nsresult
|
||||
EnumerateUSBDevices(nsTArray<USBDeviceInfo>& aDevices)
|
||||
{
|
||||
aDevices.Clear();
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
already_AddRefed<USBDeviceHandle>
|
||||
CreateUSBDeviceHandle(const nsAString& aDevicePath)
|
||||
{
|
||||
(void)aDevicePath;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
109
dom/usb/USBBackend.h
Normal file
109
dom/usb/USBBackend.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
#ifndef mozilla_dom_USBBackend_h
|
||||
#define mozilla_dom_USBBackend_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nsError.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct USBDeviceInfo {
|
||||
uint16_t mVendorId = 0;
|
||||
uint16_t mProductId = 0;
|
||||
uint8_t mUSBVersionMajor = 0;
|
||||
uint8_t mUSBVersionMinor = 0;
|
||||
uint8_t mUSBVersionSubminor = 0;
|
||||
uint8_t mDeviceClass = 0;
|
||||
uint8_t mDeviceSubclass = 0;
|
||||
uint8_t mDeviceProtocol = 0;
|
||||
uint8_t mDeviceVersionMajor = 0;
|
||||
uint8_t mDeviceVersionMinor = 0;
|
||||
uint8_t mDeviceVersionSubminor = 0;
|
||||
nsString mManufacturerName;
|
||||
nsString mProductName;
|
||||
nsString mSerialNumber;
|
||||
nsString mDevicePath;
|
||||
};
|
||||
|
||||
struct USBEndpointInfo {
|
||||
uint8_t mNumber = 0;
|
||||
uint8_t mDirection = 0; // 0 = in, 1 = out
|
||||
uint8_t mType = 0; // 1 = isochronous, 2 = bulk, 3 = interrupt
|
||||
uint16_t mPacketSize = 0;
|
||||
};
|
||||
|
||||
struct USBAlternateInterfaceInfo {
|
||||
uint8_t mSetting = 0;
|
||||
uint8_t mClass = 0;
|
||||
uint8_t mSubclass = 0;
|
||||
uint8_t mProtocol = 0;
|
||||
nsTArray<USBEndpointInfo> mEndpoints;
|
||||
};
|
||||
|
||||
struct USBInterfaceInfo {
|
||||
uint8_t mNumber = 0;
|
||||
nsTArray<USBAlternateInterfaceInfo> mAlternates;
|
||||
};
|
||||
|
||||
struct USBConfigurationInfo {
|
||||
uint8_t mValue = 0;
|
||||
nsTArray<USBInterfaceInfo> mInterfaces;
|
||||
};
|
||||
|
||||
struct USBControlTransferInfo {
|
||||
uint8_t mRequestType = 0;
|
||||
uint8_t mRecipient = 0;
|
||||
uint8_t mRequest = 0;
|
||||
uint16_t mValue = 0;
|
||||
uint16_t mIndex = 0;
|
||||
};
|
||||
|
||||
class USBDeviceHandle : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
virtual nsresult Open() = 0;
|
||||
virtual void Close() = 0;
|
||||
virtual nsresult GetConfigurations(
|
||||
nsTArray<USBConfigurationInfo>& aConfigurations) = 0;
|
||||
virtual nsresult SelectConfiguration(uint8_t aConfigurationValue) = 0;
|
||||
virtual nsresult ClaimInterface(uint8_t aInterfaceNumber) = 0;
|
||||
virtual nsresult ReleaseInterface(uint8_t aInterfaceNumber) = 0;
|
||||
virtual nsresult SelectAlternateInterface(uint8_t aInterfaceNumber,
|
||||
uint8_t aAlternateSetting) = 0;
|
||||
virtual nsresult ClearHalt(bool aIn, uint8_t aEndpointNumber) = 0;
|
||||
virtual nsresult Reset() = 0;
|
||||
virtual nsresult ControlTransferIn(const USBControlTransferInfo& aSetup,
|
||||
uint16_t aLength,
|
||||
nsTArray<uint8_t>& aData) = 0;
|
||||
virtual nsresult ControlTransferOut(const USBControlTransferInfo& aSetup,
|
||||
const nsTArray<uint8_t>& aData,
|
||||
uint32_t& aWritten) = 0;
|
||||
virtual nsresult TransferIn(uint8_t aEndpoint, uint32_t aLength,
|
||||
nsTArray<uint8_t>& aData) = 0;
|
||||
virtual nsresult TransferOut(uint8_t aEndpoint,
|
||||
const nsTArray<uint8_t>& aData,
|
||||
uint32_t& aWritten) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~USBDeviceHandle() = default;
|
||||
};
|
||||
|
||||
// Enumeration returns stable device metadata and a native path. Native
|
||||
// handles are created only after the caller has obtained permission.
|
||||
nsresult EnumerateUSBDevices(nsTArray<USBDeviceInfo>& aDevices);
|
||||
|
||||
already_AddRefed<USBDeviceHandle>
|
||||
CreateUSBDeviceHandle(const nsAString& aDevicePath);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_USBBackend_h
|
||||
471
dom/usb/USBBackendWin.cpp
Normal file
471
dom/usb/USBBackendWin.cpp
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
#include "USBBackend.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
||||
#include <setupapi.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <winusb.h>
|
||||
|
||||
#include "mozilla/fallible.h"
|
||||
#include "nsError.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
const GUID kUSBDeviceInterface = {
|
||||
0xA5DCBF10, 0x6530, 0x11D2,
|
||||
{ 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED }
|
||||
};
|
||||
|
||||
class WinUSBDeviceHandle final : public USBDeviceHandle
|
||||
{
|
||||
public:
|
||||
explicit WinUSBDeviceHandle(const nsAString& aPath)
|
||||
: mPath(aPath)
|
||||
, mDevice(INVALID_HANDLE_VALUE)
|
||||
, mInterface(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsresult Open() override
|
||||
{
|
||||
if (mPath.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mDevice = CreateFileW(reinterpret_cast<const wchar_t*>(mPath.get()),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
if (mDevice == INVALID_HANDLE_VALUE) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!WinUsb_Initialize(mDevice, &mInterface)) {
|
||||
CloseHandle(mDevice);
|
||||
mDevice = INVALID_HANDLE_VALUE;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Close() override
|
||||
{
|
||||
if (mInterface) {
|
||||
WinUsb_Free(mInterface);
|
||||
mInterface = nullptr;
|
||||
}
|
||||
if (mDevice != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(mDevice);
|
||||
mDevice = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult GetConfigurations(
|
||||
nsTArray<USBConfigurationInfo>& aConfigurations) override
|
||||
{
|
||||
aConfigurations.Clear();
|
||||
if (!mInterface) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
USB_DEVICE_DESCRIPTOR deviceDescriptor;
|
||||
ULONG transferred = 0;
|
||||
if (!WinUsb_GetDescriptor(mInterface, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0,
|
||||
reinterpret_cast<PUCHAR>(&deviceDescriptor),
|
||||
sizeof(deviceDescriptor), &transferred) ||
|
||||
transferred < sizeof(deviceDescriptor)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (uint8_t index = 0; index < deviceDescriptor.bNumConfigurations;
|
||||
++index) {
|
||||
uint8_t header[9];
|
||||
if (!WinUsb_GetDescriptor(mInterface, USB_CONFIGURATION_DESCRIPTOR_TYPE,
|
||||
index, 0, header, sizeof(header), &transferred) ||
|
||||
transferred < sizeof(header)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16_t totalLength = static_cast<uint16_t>(header[2] |
|
||||
(header[3] << 8));
|
||||
if (totalLength < sizeof(header)) {
|
||||
continue;
|
||||
}
|
||||
nsTArray<uint8_t> descriptor;
|
||||
if (!descriptor.SetLength(totalLength, mozilla::fallible) ||
|
||||
!WinUsb_GetDescriptor(mInterface, USB_CONFIGURATION_DESCRIPTOR_TYPE,
|
||||
index, 0, descriptor.Elements(), totalLength,
|
||||
&transferred) ||
|
||||
transferred < sizeof(header)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
USBConfigurationInfo configuration;
|
||||
configuration.mValue = descriptor[5];
|
||||
USBInterfaceInfo* currentInterface = nullptr;
|
||||
USBAlternateInterfaceInfo* currentAlternate = nullptr;
|
||||
uint32_t offset = descriptor[0];
|
||||
while (offset + 2 <= transferred) {
|
||||
uint8_t length = descriptor[offset];
|
||||
uint8_t type = descriptor[offset + 1];
|
||||
if (!length || offset + length > transferred) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == USB_INTERFACE_DESCRIPTOR_TYPE && length >= 9) {
|
||||
USBInterfaceInfo interfaceInfo;
|
||||
interfaceInfo.mNumber = descriptor[offset + 2];
|
||||
USBAlternateInterfaceInfo alternate;
|
||||
alternate.mSetting = descriptor[offset + 3];
|
||||
alternate.mClass = descriptor[offset + 5];
|
||||
alternate.mSubclass = descriptor[offset + 6];
|
||||
alternate.mProtocol = descriptor[offset + 7];
|
||||
configuration.mInterfaces.AppendElement(Move(interfaceInfo));
|
||||
currentInterface = &configuration.mInterfaces.LastElement();
|
||||
currentInterface->mAlternates.AppendElement(Move(alternate));
|
||||
currentAlternate = ¤tInterface->mAlternates.LastElement();
|
||||
} else if (type == USB_ENDPOINT_DESCRIPTOR_TYPE && length >= 7 &&
|
||||
currentAlternate) {
|
||||
USBEndpointInfo endpoint;
|
||||
uint8_t address = descriptor[offset + 2];
|
||||
endpoint.mNumber = address & 0x0f;
|
||||
endpoint.mDirection = (address & 0x80) ? 0 : 1;
|
||||
endpoint.mType = descriptor[offset + 3] & 0x03;
|
||||
endpoint.mPacketSize = static_cast<uint16_t>(
|
||||
descriptor[offset + 4] | (descriptor[offset + 5] << 8));
|
||||
if (endpoint.mType) {
|
||||
currentAlternate->mEndpoints.AppendElement(Move(endpoint));
|
||||
}
|
||||
}
|
||||
offset += length;
|
||||
}
|
||||
aConfigurations.AppendElement(Move(configuration));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult SelectConfiguration(uint8_t aConfigurationValue) override
|
||||
{
|
||||
if (!mInterface) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
WINUSB_SETUP_PACKET setup;
|
||||
setup.RequestType = 0x00; // Host-to-device, standard, device.
|
||||
setup.Request = 9; // SET_CONFIGURATION.
|
||||
setup.Value = aConfigurationValue;
|
||||
setup.Index = 0;
|
||||
setup.Length = 0;
|
||||
ULONG transferred = 0;
|
||||
return WinUsb_ControlTransfer(mInterface, setup, nullptr, 0,
|
||||
&transferred, nullptr)
|
||||
? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult ClaimInterface(uint8_t aInterfaceNumber) override
|
||||
{
|
||||
return mInterface && WinUsb_ClaimInterface(mInterface, aInterfaceNumber)
|
||||
? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult ReleaseInterface(uint8_t aInterfaceNumber) override
|
||||
{
|
||||
return mInterface && WinUsb_ReleaseInterface(mInterface, aInterfaceNumber)
|
||||
? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult SelectAlternateInterface(uint8_t aInterfaceNumber,
|
||||
uint8_t aAlternateSetting) override
|
||||
{
|
||||
return mInterface &&
|
||||
WinUsb_SetCurrentAlternateSetting(mInterface, aInterfaceNumber,
|
||||
aAlternateSetting)
|
||||
? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult ClearHalt(bool aIn, uint8_t aEndpointNumber) override
|
||||
{
|
||||
uint8_t pipe = aEndpointNumber & 0x0f;
|
||||
if (aIn) {
|
||||
pipe |= 0x80;
|
||||
}
|
||||
return mInterface && WinUsb_ResetPipe(mInterface, pipe)
|
||||
? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult Reset() override
|
||||
{
|
||||
return mInterface && WinUsb_ResetDevice(mInterface)
|
||||
? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult ControlTransferIn(const USBControlTransferInfo& aSetup,
|
||||
uint16_t aLength,
|
||||
nsTArray<uint8_t>& aData) override
|
||||
{
|
||||
if (!mInterface) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
aData.Clear();
|
||||
if (!aData.SetLength(aLength, mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
WINUSB_SETUP_PACKET setup;
|
||||
setup.RequestType = static_cast<UCHAR>(0x80 |
|
||||
((aSetup.mRequestType & 0x03) << 5) | (aSetup.mRecipient & 0x1f));
|
||||
setup.Request = aSetup.mRequest;
|
||||
setup.Value = aSetup.mValue;
|
||||
setup.Index = aSetup.mIndex;
|
||||
setup.Length = aLength;
|
||||
ULONG transferred = 0;
|
||||
if (!WinUsb_ControlTransfer(mInterface, setup, aData.Elements(), aLength,
|
||||
&transferred, nullptr)) {
|
||||
aData.Clear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
aData.SetLength(transferred);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ControlTransferOut(const USBControlTransferInfo& aSetup,
|
||||
const nsTArray<uint8_t>& aData,
|
||||
uint32_t& aWritten) override
|
||||
{
|
||||
aWritten = 0;
|
||||
if (!mInterface) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
WINUSB_SETUP_PACKET setup;
|
||||
setup.RequestType = static_cast<UCHAR>(
|
||||
((aSetup.mRequestType & 0x03) << 5) | (aSetup.mRecipient & 0x1f));
|
||||
setup.Request = aSetup.mRequest;
|
||||
setup.Value = aSetup.mValue;
|
||||
setup.Index = aSetup.mIndex;
|
||||
setup.Length = static_cast<USHORT>(aData.Length());
|
||||
ULONG transferred = 0;
|
||||
PUCHAR data = aData.IsEmpty()
|
||||
? nullptr : const_cast<PUCHAR>(aData.Elements());
|
||||
if (!WinUsb_ControlTransfer(mInterface, setup, data, aData.Length(),
|
||||
&transferred, nullptr)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
aWritten = transferred;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult TransferIn(uint8_t aEndpoint, uint32_t aLength,
|
||||
nsTArray<uint8_t>& aData) override
|
||||
{
|
||||
if (!mInterface) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
if (!aData.SetLength(aLength, mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
ULONG transferred = 0;
|
||||
if (!WinUsb_ReadPipe(mInterface, aEndpoint | 0x80, aData.Elements(),
|
||||
aLength, &transferred, nullptr)) {
|
||||
aData.Clear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
aData.SetLength(transferred);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult TransferOut(uint8_t aEndpoint, const nsTArray<uint8_t>& aData,
|
||||
uint32_t& aWritten) override
|
||||
{
|
||||
aWritten = 0;
|
||||
if (!mInterface) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
ULONG transferred = 0;
|
||||
PUCHAR data = aData.IsEmpty()
|
||||
? nullptr : const_cast<PUCHAR>(aData.Elements());
|
||||
if (!WinUsb_WritePipe(mInterface, aEndpoint, data, aData.Length(),
|
||||
&transferred, nullptr)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
aWritten = transferred;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~WinUSBDeviceHandle() override
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
nsString mPath;
|
||||
HANDLE mDevice;
|
||||
WINUSB_INTERFACE_HANDLE mInterface;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(WinUSBDeviceHandle, USBDeviceHandle)
|
||||
|
||||
bool
|
||||
ParseHex(const wchar_t* aValue, uint32_t aLength, uint16_t& aResult)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
for (uint32_t i = 0; i < aLength; ++i) {
|
||||
wchar_t c = aValue[i];
|
||||
uint32_t digit;
|
||||
if (c >= L'0' && c <= L'9') {
|
||||
digit = c - L'0';
|
||||
} else if (c >= L'A' && c <= L'F') {
|
||||
digit = c - L'A' + 10;
|
||||
} else if (c >= L'a' && c <= L'f') {
|
||||
digit = c - L'a' + 10;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
value = (value << 4) | digit;
|
||||
}
|
||||
|
||||
if (value > 0xffff) {
|
||||
return false;
|
||||
}
|
||||
aResult = static_cast<uint16_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CopyRegistryString(HDEVINFO aDevices,
|
||||
SP_DEVINFO_DATA& aDevice,
|
||||
DWORD aProperty,
|
||||
nsString& aValue)
|
||||
{
|
||||
DWORD type = 0;
|
||||
DWORD size = 0;
|
||||
if (!SetupDiGetDeviceRegistryPropertyW(aDevices, &aDevice, aProperty,
|
||||
&type, nullptr, 0, &size) ||
|
||||
GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
|
||||
(type != REG_SZ && type != REG_MULTI_SZ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<wchar_t> buffer;
|
||||
if (!buffer.SetLength((size / sizeof(wchar_t)) + 1, mozilla::fallible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SetupDiGetDeviceRegistryPropertyW(aDevices, &aDevice, aProperty,
|
||||
&type,
|
||||
reinterpret_cast<BYTE*>(buffer.Elements()),
|
||||
size, nullptr)) {
|
||||
aValue.Assign(reinterpret_cast<const char16_t*>(buffer.Elements()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ParseHardwareId(const nsString& aHardwareId, USBDeviceInfo& aInfo)
|
||||
{
|
||||
const char16_t* id = aHardwareId.BeginReading();
|
||||
uint32_t length = aHardwareId.Length();
|
||||
|
||||
for (uint32_t i = 0; i + 8 <= length; ++i) {
|
||||
if ((id[i] == 'V' || id[i] == 'v') &&
|
||||
(id[i + 1] == 'I' || id[i + 1] == 'i') &&
|
||||
(id[i + 2] == 'D' || id[i + 2] == 'd') && id[i + 3] == '_') {
|
||||
uint16_t value;
|
||||
if (ParseHex(reinterpret_cast<const wchar_t*>(id + i + 4), 4, value)) {
|
||||
aInfo.mVendorId = value;
|
||||
}
|
||||
}
|
||||
|
||||
if ((id[i] == 'P' || id[i] == 'p') &&
|
||||
(id[i + 1] == 'I' || id[i + 1] == 'i') &&
|
||||
(id[i + 2] == 'D' || id[i + 2] == 'd') && id[i + 3] == '_') {
|
||||
uint16_t value;
|
||||
if (ParseHex(reinterpret_cast<const wchar_t*>(id + i + 4), 4, value)) {
|
||||
aInfo.mProductId = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
nsresult
|
||||
EnumerateUSBDevices(nsTArray<USBDeviceInfo>& aDevices)
|
||||
{
|
||||
aDevices.Clear();
|
||||
|
||||
HDEVINFO devices = SetupDiGetClassDevsW(&kUSBDeviceInterface, nullptr,
|
||||
nullptr,
|
||||
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
if (devices == INVALID_HANDLE_VALUE) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (DWORD index = 0; ; ++index) {
|
||||
SP_DEVICE_INTERFACE_DATA interfaceData;
|
||||
memset(&interfaceData, 0, sizeof(interfaceData));
|
||||
interfaceData.cbSize = sizeof(interfaceData);
|
||||
if (!SetupDiEnumDeviceInterfaces(devices, nullptr, &kUSBDeviceInterface,
|
||||
index, &interfaceData)) {
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD detailSize = 0;
|
||||
SetupDiGetDeviceInterfaceDetailW(devices, &interfaceData, nullptr, 0,
|
||||
&detailSize, nullptr);
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || !detailSize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsTArray<BYTE> detailBuffer;
|
||||
if (!detailBuffer.SetLength(detailSize, mozilla::fallible)) {
|
||||
continue;
|
||||
}
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_W* detail =
|
||||
reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_W*>(detailBuffer.Elements());
|
||||
detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
|
||||
|
||||
SP_DEVINFO_DATA device;
|
||||
memset(&device, 0, sizeof(device));
|
||||
device.cbSize = sizeof(device);
|
||||
if (!SetupDiGetDeviceInterfaceDetailW(devices, &interfaceData, detail,
|
||||
detailSize, nullptr, &device)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsString hardwareId;
|
||||
CopyRegistryString(devices, device, SPDRP_HARDWAREID, hardwareId);
|
||||
|
||||
USBDeviceInfo info;
|
||||
ParseHardwareId(hardwareId, info);
|
||||
if (!info.mVendorId && !info.mProductId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CopyRegistryString(devices, device, SPDRP_MFG, info.mManufacturerName);
|
||||
CopyRegistryString(devices, device, SPDRP_DEVICEDESC, info.mProductName);
|
||||
info.mDevicePath.Assign(reinterpret_cast<const char16_t*>(detail->DevicePath));
|
||||
aDevices.AppendElement(Move(info));
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(devices);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<USBDeviceHandle>
|
||||
CreateUSBDeviceHandle(const nsAString& aDevicePath)
|
||||
{
|
||||
RefPtr<USBDeviceHandle> handle = new WinUSBDeviceHandle(aDevicePath);
|
||||
return handle.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // XP_WIN
|
||||
209
dom/usb/USBDescriptors.cpp
Normal file
209
dom/usb/USBDescriptors.cpp
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
#include "USBDescriptors.h"
|
||||
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
#include "mozilla/dom/ArrayBuffer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(USBEndpoint)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(USBEndpoint)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(USBEndpoint)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USBEndpoint)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
USBEndpoint::USBEndpoint(nsIGlobalObject* aOwner, uint8_t aNumber,
|
||||
USBDirection aDirection, USBEndpointType aType,
|
||||
uint16_t aPacketSize)
|
||||
: mOwner(aOwner)
|
||||
, mNumber(aNumber)
|
||||
, mDirection(aDirection)
|
||||
, mType(aType)
|
||||
, mPacketSize(aPacketSize)
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
USBEndpoint::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return USBEndpointBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(USBAlternateInterface, mEndpoints)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(USBAlternateInterface)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(USBAlternateInterface)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USBAlternateInterface)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
USBAlternateInterface::USBAlternateInterface(
|
||||
nsIGlobalObject* aOwner, uint8_t aSetting, uint8_t aClass,
|
||||
uint8_t aSubclass, uint8_t aProtocol,
|
||||
nsTArray<RefPtr<USBEndpoint>>&& aEndpoints)
|
||||
: mOwner(aOwner)
|
||||
, mSetting(aSetting)
|
||||
, mClass(aClass)
|
||||
, mSubclass(aSubclass)
|
||||
, mProtocol(aProtocol)
|
||||
, mEndpoints(Move(aEndpoints))
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
USBAlternateInterface::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return USBAlternateInterfaceBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(USBInterface, mAlternate, mAlternates)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(USBInterface)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(USBInterface)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USBInterface)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
USBInterface::USBInterface(
|
||||
nsIGlobalObject* aOwner, uint8_t aNumber,
|
||||
nsTArray<RefPtr<USBAlternateInterface>>&& aAlternates)
|
||||
: mOwner(aOwner)
|
||||
, mNumber(aNumber)
|
||||
, mAlternates(Move(aAlternates))
|
||||
, mClaimed(false)
|
||||
{
|
||||
if (!mAlternates.IsEmpty()) {
|
||||
mAlternate = mAlternates[0];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
USBInterface::SetAlternate(uint8_t aSetting)
|
||||
{
|
||||
for (const auto& alternate : mAlternates) {
|
||||
if (alternate->AlternateSetting() == aSetting) {
|
||||
mAlternate = alternate;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSObject*
|
||||
USBInterface::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return USBInterfaceBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(USBConfiguration, mInterfaces)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(USBConfiguration)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(USBConfiguration)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USBConfiguration)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
USBConfiguration::USBConfiguration(
|
||||
nsIGlobalObject* aOwner, uint8_t aValue,
|
||||
nsTArray<RefPtr<USBInterface>>&& aInterfaces)
|
||||
: mOwner(aOwner)
|
||||
, mValue(aValue)
|
||||
, mInterfaces(Move(aInterfaces))
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
USBConfiguration::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return USBConfigurationBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(USBInTransferResult)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(USBInTransferResult)
|
||||
tmp->mData = nullptr;
|
||||
mozilla::DropJSObjects(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(USBInTransferResult)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(USBInTransferResult)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(USBInTransferResult)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(USBInTransferResult)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USBInTransferResult)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
USBInTransferResult::USBInTransferResult(
|
||||
nsIGlobalObject* aOwner, USBTransferStatus aStatus,
|
||||
const nsTArray<uint8_t>& aData)
|
||||
: mOwner(aOwner)
|
||||
, mStatus(aStatus)
|
||||
, mRawData(aData)
|
||||
, mData(nullptr)
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
USBInTransferResult::~USBInTransferResult()
|
||||
{
|
||||
mData = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
void
|
||||
USBInTransferResult::GetData(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aData,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!mData) {
|
||||
mData = ArrayBuffer::Create(aCx, this, mRawData.Length(),
|
||||
mRawData.Elements());
|
||||
if (!mData) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
mRawData.Clear();
|
||||
}
|
||||
aData.set(mData);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
USBInTransferResult::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return USBInTransferResultBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(USBOutTransferResult, mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(USBOutTransferResult)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(USBOutTransferResult)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USBOutTransferResult)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
USBOutTransferResult::USBOutTransferResult(nsIGlobalObject* aOwner,
|
||||
USBTransferStatus aStatus,
|
||||
uint32_t aBytesWritten)
|
||||
: mOwner(aOwner)
|
||||
, mStatus(aStatus)
|
||||
, mBytesWritten(aBytesWritten)
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
USBOutTransferResult::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return USBOutTransferResultBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
200
dom/usb/USBDescriptors.h
Normal file
200
dom/usb/USBDescriptors.h
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
#ifndef mozilla_dom_USBDescriptors_h
|
||||
#define mozilla_dom_USBDescriptors_h
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "mozilla/dom/USBBinding.h"
|
||||
#include "mozilla/dom/USBAlternateInterfaceBinding.h"
|
||||
#include "mozilla/dom/USBConfigurationBinding.h"
|
||||
#include "mozilla/dom/USBEndpointBinding.h"
|
||||
#include "mozilla/dom/USBInterfaceBinding.h"
|
||||
#include "mozilla/dom/USBInTransferResultBinding.h"
|
||||
#include "mozilla/dom/USBOutTransferResultBinding.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class USBEndpoint final : public nsISupports, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
USBEndpoint(nsIGlobalObject* aOwner, uint8_t aNumber,
|
||||
USBDirection aDirection, USBEndpointType aType,
|
||||
uint16_t aPacketSize);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(USBEndpoint)
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
uint8_t EndpointNumber() const { return mNumber; }
|
||||
USBDirection Direction() const { return mDirection; }
|
||||
USBEndpointType Type() const { return mType; }
|
||||
uint16_t PacketSize() const { return mPacketSize; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
~USBEndpoint() = default;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
uint8_t mNumber;
|
||||
USBDirection mDirection;
|
||||
USBEndpointType mType;
|
||||
uint16_t mPacketSize;
|
||||
};
|
||||
|
||||
class USBAlternateInterface final : public nsISupports, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
USBAlternateInterface(nsIGlobalObject* aOwner, uint8_t aSetting,
|
||||
uint8_t aClass, uint8_t aSubclass, uint8_t aProtocol,
|
||||
nsTArray<RefPtr<USBEndpoint>>&& aEndpoints);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(USBAlternateInterface)
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
uint8_t AlternateSetting() const { return mSetting; }
|
||||
uint8_t InterfaceClass() const { return mClass; }
|
||||
uint8_t InterfaceSubclass() const { return mSubclass; }
|
||||
uint8_t InterfaceProtocol() const { return mProtocol; }
|
||||
void GetInterfaceName(Nullable<nsString>& aValue) const
|
||||
{ aValue = mName; }
|
||||
void GetEndpoints(nsTArray<RefPtr<USBEndpoint>>& aValue) const
|
||||
{ aValue = mEndpoints; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
~USBAlternateInterface() = default;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
uint8_t mSetting;
|
||||
uint8_t mClass;
|
||||
uint8_t mSubclass;
|
||||
uint8_t mProtocol;
|
||||
Nullable<nsString> mName;
|
||||
nsTArray<RefPtr<USBEndpoint>> mEndpoints;
|
||||
};
|
||||
|
||||
class USBInterface final : public nsISupports, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
USBInterface(nsIGlobalObject* aOwner, uint8_t aNumber,
|
||||
nsTArray<RefPtr<USBAlternateInterface>>&& aAlternates);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(USBInterface)
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
uint8_t InterfaceNumber() const { return mNumber; }
|
||||
USBAlternateInterface* GetAlternate() const { return mAlternate; }
|
||||
void SetAlternate(uint8_t aSetting);
|
||||
void GetAlternates(nsTArray<RefPtr<USBAlternateInterface>>& aValue) const
|
||||
{ aValue = mAlternates; }
|
||||
bool Claimed() const { return mClaimed; }
|
||||
void SetClaimed(bool aClaimed) { mClaimed = aClaimed; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
~USBInterface() = default;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
uint8_t mNumber;
|
||||
RefPtr<USBAlternateInterface> mAlternate;
|
||||
nsTArray<RefPtr<USBAlternateInterface>> mAlternates;
|
||||
bool mClaimed;
|
||||
};
|
||||
|
||||
class USBConfiguration final : public nsISupports, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
USBConfiguration(nsIGlobalObject* aOwner, uint8_t aValue,
|
||||
nsTArray<RefPtr<USBInterface>>&& aInterfaces);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(USBConfiguration)
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
uint8_t ConfigurationValue() const { return mValue; }
|
||||
void GetConfigurationName(Nullable<nsString>& aValue) const
|
||||
{ aValue = mName; }
|
||||
void GetInterfaces(nsTArray<RefPtr<USBInterface>>& aValue) const
|
||||
{ aValue = mInterfaces; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
~USBConfiguration() = default;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
uint8_t mValue;
|
||||
Nullable<nsString> mName;
|
||||
nsTArray<RefPtr<USBInterface>> mInterfaces;
|
||||
};
|
||||
|
||||
class USBInTransferResult final : public nsISupports, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
USBInTransferResult(nsIGlobalObject* aOwner, USBTransferStatus aStatus,
|
||||
const nsTArray<uint8_t>& aData);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(USBInTransferResult)
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
USBTransferStatus Status() const { return mStatus; }
|
||||
void GetData(JSContext* aCx, JS::MutableHandle<JSObject*> aData,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
~USBInTransferResult();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
USBTransferStatus mStatus;
|
||||
nsTArray<uint8_t> mRawData;
|
||||
JS::Heap<JSObject*> mData;
|
||||
};
|
||||
|
||||
class USBOutTransferResult final : public nsISupports, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
USBOutTransferResult(nsIGlobalObject* aOwner, USBTransferStatus aStatus,
|
||||
uint32_t aBytesWritten);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(USBOutTransferResult)
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
USBTransferStatus Status() const { return mStatus; }
|
||||
uint32_t BytesWritten() const { return mBytesWritten; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
~USBOutTransferResult() = default;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
USBTransferStatus mStatus;
|
||||
uint32_t mBytesWritten;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_USBDescriptors_h
|
||||
477
dom/usb/USBDevice.cpp
Normal file
477
dom/usb/USBDevice.cpp
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
#include "USBDevice.h"
|
||||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/USB.h"
|
||||
#include "mozilla/dom/USBDeviceBinding.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
already_AddRefed<Promise>
|
||||
BackendPromise(nsIGlobalObject* aOwner, nsresult aResult, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(aOwner, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (NS_SUCCEEDED(aResult)) {
|
||||
promise->MaybeResolve();
|
||||
} else {
|
||||
promise->MaybeReject(aResult);
|
||||
}
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
USBInterface*
|
||||
FindInterface(USBConfiguration* aConfiguration, uint8_t aNumber)
|
||||
{
|
||||
if (!aConfiguration) {
|
||||
return nullptr;
|
||||
}
|
||||
nsTArray<RefPtr<USBInterface>> interfaces;
|
||||
aConfiguration->GetInterfaces(interfaces);
|
||||
for (const auto& interfaceObject : interfaces) {
|
||||
if (interfaceObject->InterfaceNumber() == aNumber) {
|
||||
return interfaceObject;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
USBControlTransferInfo
|
||||
MakeControlTransferInfo(const USBControlTransferParameters& aSetup)
|
||||
{
|
||||
USBControlTransferInfo setup;
|
||||
setup.mRequestType = static_cast<uint8_t>(aSetup.mRequestType);
|
||||
setup.mRecipient = static_cast<uint8_t>(aSetup.mRecipient);
|
||||
setup.mRequest = aSetup.mRequest;
|
||||
setup.mValue = aSetup.mValue;
|
||||
setup.mIndex = aSetup.mIndex;
|
||||
return setup;
|
||||
}
|
||||
|
||||
void
|
||||
CopyTransferData(const ArrayBufferViewOrArrayBuffer& aData,
|
||||
nsTArray<uint8_t>& aOutput)
|
||||
{
|
||||
aOutput.Clear();
|
||||
if (aData.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = aData.GetAsArrayBuffer();
|
||||
buffer.ComputeLengthAndData();
|
||||
aOutput.AppendElements(buffer.Data(), buffer.Length());
|
||||
} else {
|
||||
const ArrayBufferView& view = aData.GetAsArrayBufferView();
|
||||
view.ComputeLengthAndData();
|
||||
aOutput.AppendElements(view.Data(), view.Length());
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(USBDevice, DOMEventTargetHelper,
|
||||
mHandle, mConfiguration, mConfigurations)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USBDevice)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(USBDevice, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(USBDevice, DOMEventTargetHelper)
|
||||
|
||||
USBDevice::USBDevice(nsIGlobalObject* aOwner)
|
||||
: DOMEventTargetHelper(aOwner)
|
||||
, mVendorId(0)
|
||||
, mProductId(0)
|
||||
, mUSBVersionMajor(0)
|
||||
, mUSBVersionMinor(0)
|
||||
, mUSBVersionSubminor(0)
|
||||
, mDeviceClass(0)
|
||||
, mDeviceSubclass(0)
|
||||
, mDeviceProtocol(0)
|
||||
, mDeviceVersionMajor(0)
|
||||
, mDeviceVersionMinor(0)
|
||||
, mDeviceVersionSubminor(0)
|
||||
, mOpened(false)
|
||||
{
|
||||
}
|
||||
|
||||
USBDevice::~USBDevice()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
USBDevice::SetDeviceInfo(const USBDeviceInfo& aInfo)
|
||||
{
|
||||
mVendorId = aInfo.mVendorId;
|
||||
mProductId = aInfo.mProductId;
|
||||
mUSBVersionMajor = aInfo.mUSBVersionMajor;
|
||||
mUSBVersionMinor = aInfo.mUSBVersionMinor;
|
||||
mUSBVersionSubminor = aInfo.mUSBVersionSubminor;
|
||||
mDeviceClass = aInfo.mDeviceClass;
|
||||
mDeviceSubclass = aInfo.mDeviceSubclass;
|
||||
mDeviceProtocol = aInfo.mDeviceProtocol;
|
||||
mDeviceVersionMajor = aInfo.mDeviceVersionMajor;
|
||||
mDeviceVersionMinor = aInfo.mDeviceVersionMinor;
|
||||
mDeviceVersionSubminor = aInfo.mDeviceVersionSubminor;
|
||||
mDevicePath = aInfo.mDevicePath;
|
||||
|
||||
if (aInfo.mManufacturerName.IsEmpty()) {
|
||||
mManufacturerName.SetNull();
|
||||
} else {
|
||||
mManufacturerName.SetValue(aInfo.mManufacturerName);
|
||||
}
|
||||
if (aInfo.mProductName.IsEmpty()) {
|
||||
mProductName.SetNull();
|
||||
} else {
|
||||
mProductName.SetValue(aInfo.mProductName);
|
||||
}
|
||||
if (aInfo.mSerialNumber.IsEmpty()) {
|
||||
mSerialNumber.SetNull();
|
||||
} else {
|
||||
mSerialNumber.SetValue(aInfo.mSerialNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
USBDevice::SetConfigurations(
|
||||
const nsTArray<USBConfigurationInfo>& aConfigurations)
|
||||
{
|
||||
mConfigurations.Clear();
|
||||
mConfiguration = nullptr;
|
||||
|
||||
for (const auto& configurationInfo : aConfigurations) {
|
||||
nsTArray<RefPtr<USBInterface>> interfaces;
|
||||
for (const auto& interfaceInfo : configurationInfo.mInterfaces) {
|
||||
nsTArray<RefPtr<USBAlternateInterface>> alternates;
|
||||
for (const auto& alternateInfo : interfaceInfo.mAlternates) {
|
||||
nsTArray<RefPtr<USBEndpoint>> endpoints;
|
||||
for (const auto& endpointInfo : alternateInfo.mEndpoints) {
|
||||
USBDirection direction = endpointInfo.mDirection == 0
|
||||
? USBDirection::In : USBDirection::Out;
|
||||
USBEndpointType type;
|
||||
switch (endpointInfo.mType) {
|
||||
case 1:
|
||||
type = USBEndpointType::Isochronous;
|
||||
break;
|
||||
case 3:
|
||||
type = USBEndpointType::Interrupt;
|
||||
break;
|
||||
default:
|
||||
type = USBEndpointType::Bulk;
|
||||
break;
|
||||
}
|
||||
endpoints.AppendElement(new USBEndpoint(
|
||||
GetOwnerGlobal(), endpointInfo.mNumber, direction, type,
|
||||
endpointInfo.mPacketSize));
|
||||
}
|
||||
alternates.AppendElement(new USBAlternateInterface(
|
||||
GetOwnerGlobal(), alternateInfo.mSetting, alternateInfo.mClass,
|
||||
alternateInfo.mSubclass, alternateInfo.mProtocol, Move(endpoints)));
|
||||
}
|
||||
interfaces.AppendElement(new USBInterface(
|
||||
GetOwnerGlobal(), interfaceInfo.mNumber, Move(alternates)));
|
||||
}
|
||||
RefPtr<USBConfiguration> configuration = new USBConfiguration(
|
||||
GetOwnerGlobal(), configurationInfo.mValue, Move(interfaces));
|
||||
if (!mConfiguration) {
|
||||
mConfiguration = configuration;
|
||||
}
|
||||
mConfigurations.AppendElement(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
JSObject*
|
||||
USBDevice::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return USBDeviceBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::Open(ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mOpened) {
|
||||
promise->MaybeResolve();
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
mHandle = CreateUSBDeviceHandle(mDevicePath);
|
||||
if (!mHandle || NS_FAILED(mHandle->Open())) {
|
||||
mHandle = nullptr;
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
mOpened = true;
|
||||
nsTArray<USBConfigurationInfo> configurations;
|
||||
if (NS_SUCCEEDED(mHandle->GetConfigurations(configurations))) {
|
||||
SetConfigurations(configurations);
|
||||
}
|
||||
promise->MaybeResolve();
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::Close(ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mHandle) {
|
||||
mHandle->Close();
|
||||
mHandle = nullptr;
|
||||
}
|
||||
mOpened = false;
|
||||
promise->MaybeResolve();
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::Forget(ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mHandle) {
|
||||
mHandle->Close();
|
||||
mHandle = nullptr;
|
||||
}
|
||||
mOpened = false;
|
||||
if (mUSB) {
|
||||
mUSB->RemoveAuthorizedDevice(this);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetOwnerGlobal());
|
||||
if (window && window->GetDoc()) {
|
||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
||||
services::GetPermissionManager();
|
||||
if (permissionManager) {
|
||||
permissionManager->RemoveFromPrincipal(window->GetDoc()->NodePrincipal(),
|
||||
"usb");
|
||||
}
|
||||
}
|
||||
promise->MaybeResolve();
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::SelectConfiguration(uint8_t aValue, ErrorResult& aRv)
|
||||
{
|
||||
if (!mOpened || !mHandle) {
|
||||
return BackendPromise(GetOwnerGlobal(), NS_ERROR_DOM_INVALID_STATE_ERR, aRv);
|
||||
}
|
||||
nsresult rv = mHandle->SelectConfiguration(aValue);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
for (const auto& configuration : mConfigurations) {
|
||||
if (configuration->ConfigurationValue() == aValue) {
|
||||
mConfiguration = configuration;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return BackendPromise(GetOwnerGlobal(), rv, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::ClaimInterface(uint8_t aNumber, ErrorResult& aRv)
|
||||
{
|
||||
if (!mOpened || !mHandle || !FindInterface(mConfiguration, aNumber)) {
|
||||
return BackendPromise(GetOwnerGlobal(), NS_ERROR_DOM_INVALID_STATE_ERR, aRv);
|
||||
}
|
||||
nsresult rv = mHandle->ClaimInterface(aNumber);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
FindInterface(mConfiguration, aNumber)->SetClaimed(true);
|
||||
}
|
||||
return BackendPromise(GetOwnerGlobal(), rv, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::ReleaseInterface(uint8_t aNumber, ErrorResult& aRv)
|
||||
{
|
||||
USBInterface* interfaceObject = FindInterface(mConfiguration, aNumber);
|
||||
if (!mOpened || !mHandle || !interfaceObject) {
|
||||
return BackendPromise(GetOwnerGlobal(), NS_ERROR_DOM_INVALID_STATE_ERR, aRv);
|
||||
}
|
||||
nsresult rv = mHandle->ReleaseInterface(aNumber);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
interfaceObject->SetClaimed(false);
|
||||
}
|
||||
return BackendPromise(GetOwnerGlobal(), rv, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::SelectAlternateInterface(uint8_t aNumber, uint8_t aSetting,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
USBInterface* interfaceObject = FindInterface(mConfiguration, aNumber);
|
||||
if (!mOpened || !mHandle || !interfaceObject) {
|
||||
return BackendPromise(GetOwnerGlobal(), NS_ERROR_DOM_INVALID_STATE_ERR, aRv);
|
||||
}
|
||||
nsresult rv = mHandle->SelectAlternateInterface(aNumber, aSetting);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
interfaceObject->SetAlternate(aSetting);
|
||||
}
|
||||
return BackendPromise(GetOwnerGlobal(), rv, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::ClearHalt(USBDirection aDirection, uint8_t aEndpoint,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!mOpened || !mHandle) {
|
||||
return BackendPromise(GetOwnerGlobal(), NS_ERROR_DOM_INVALID_STATE_ERR, aRv);
|
||||
}
|
||||
return BackendPromise(GetOwnerGlobal(),
|
||||
mHandle->ClearHalt(aDirection == USBDirection::In,
|
||||
aEndpoint), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::Reset(ErrorResult& aRv)
|
||||
{
|
||||
if (!mOpened || !mHandle) {
|
||||
return BackendPromise(GetOwnerGlobal(), NS_ERROR_DOM_INVALID_STATE_ERR, aRv);
|
||||
}
|
||||
nsresult rv = mHandle->Reset();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsTArray<RefPtr<USBInterface>> interfaces;
|
||||
if (mConfiguration) {
|
||||
mConfiguration->GetInterfaces(interfaces);
|
||||
for (const auto& interfaceObject : interfaces) {
|
||||
interfaceObject->SetClaimed(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return BackendPromise(GetOwnerGlobal(), rv, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::ControlTransferIn(const USBControlTransferParameters& aSetup,
|
||||
uint16_t aLength, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!mOpened || !mHandle) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> data;
|
||||
nsresult rv = mHandle->ControlTransferIn(
|
||||
MakeControlTransferInfo(aSetup), aLength, data);
|
||||
if (NS_FAILED(rv)) {
|
||||
promise->MaybeReject(rv);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
RefPtr<USBInTransferResult> result = new USBInTransferResult(
|
||||
GetOwnerGlobal(), USBTransferStatus::Ok, data);
|
||||
promise->MaybeResolve(result);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::ControlTransferOut(
|
||||
const USBControlTransferParameters& aSetup,
|
||||
const Optional<ArrayBufferViewOrArrayBuffer>& aData, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!mOpened || !mHandle) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> data;
|
||||
if (aData.WasPassed()) {
|
||||
CopyTransferData(aData.Value(), data);
|
||||
}
|
||||
uint32_t written = 0;
|
||||
nsresult rv = mHandle->ControlTransferOut(
|
||||
MakeControlTransferInfo(aSetup), data, written);
|
||||
if (NS_FAILED(rv)) {
|
||||
promise->MaybeReject(rv);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
RefPtr<USBOutTransferResult> result = new USBOutTransferResult(
|
||||
GetOwnerGlobal(), USBTransferStatus::Ok, written);
|
||||
promise->MaybeResolve(result);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::TransferIn(uint8_t aEndpoint, uint32_t aLength, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!mOpened || !mHandle) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> data;
|
||||
nsresult rv = mHandle->TransferIn(aEndpoint, aLength, data);
|
||||
if (NS_FAILED(rv)) {
|
||||
promise->MaybeReject(rv);
|
||||
return promise.forget();
|
||||
}
|
||||
RefPtr<USBInTransferResult> result = new USBInTransferResult(
|
||||
GetOwnerGlobal(), USBTransferStatus::Ok, data);
|
||||
promise->MaybeResolve(result);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
USBDevice::TransferOut(uint8_t aEndpoint,
|
||||
const ArrayBufferViewOrArrayBuffer& aData,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = Promise::Create(GetOwnerGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!mOpened || !mHandle) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> data;
|
||||
CopyTransferData(aData, data);
|
||||
uint32_t written = 0;
|
||||
nsresult rv = mHandle->TransferOut(aEndpoint, data, written);
|
||||
if (NS_FAILED(rv)) {
|
||||
promise->MaybeReject(rv);
|
||||
return promise.forget();
|
||||
}
|
||||
RefPtr<USBOutTransferResult> result = new USBOutTransferResult(
|
||||
GetOwnerGlobal(), USBTransferStatus::Ok, written);
|
||||
promise->MaybeResolve(result);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
115
dom/usb/USBDevice.h
Normal file
115
dom/usb/USBDevice.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
#ifndef mozilla_dom_USBDevice_h
|
||||
#define mozilla_dom_USBDevice_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/USBBackend.h"
|
||||
#include "mozilla/dom/USBDescriptors.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "nsString.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class USB;
|
||||
class ArrayBufferViewOrArrayBuffer;
|
||||
struct USBControlTransferParameters;
|
||||
|
||||
class USBDevice final : public DOMEventTargetHelper
|
||||
{
|
||||
uint16_t mVendorId;
|
||||
uint16_t mProductId;
|
||||
uint8_t mUSBVersionMajor;
|
||||
uint8_t mUSBVersionMinor;
|
||||
uint8_t mUSBVersionSubminor;
|
||||
uint8_t mDeviceClass;
|
||||
uint8_t mDeviceSubclass;
|
||||
uint8_t mDeviceProtocol;
|
||||
uint8_t mDeviceVersionMajor;
|
||||
uint8_t mDeviceVersionMinor;
|
||||
uint8_t mDeviceVersionSubminor;
|
||||
Nullable<nsString> mManufacturerName;
|
||||
Nullable<nsString> mProductName;
|
||||
Nullable<nsString> mSerialNumber;
|
||||
nsString mDevicePath;
|
||||
RefPtr<USBDeviceHandle> mHandle;
|
||||
RefPtr<USBConfiguration> mConfiguration;
|
||||
nsTArray<RefPtr<USBConfiguration>> mConfigurations;
|
||||
USB* mUSB = nullptr;
|
||||
bool mOpened;
|
||||
|
||||
public:
|
||||
explicit USBDevice(nsIGlobalObject* aOwner);
|
||||
|
||||
void SetDeviceInfo(const USBDeviceInfo& aInfo);
|
||||
void SetConfigurations(const nsTArray<USBConfigurationInfo>& aConfigurations);
|
||||
void SetUSB(USB* aUSB) { mUSB = aUSB; }
|
||||
void ClearUSB() { mUSB = nullptr; }
|
||||
|
||||
uint16_t VendorId() const { return mVendorId; }
|
||||
uint16_t ProductId() const { return mProductId; }
|
||||
uint8_t USBVersionMajor() const { return mUSBVersionMajor; }
|
||||
uint8_t USBVersionMinor() const { return mUSBVersionMinor; }
|
||||
uint8_t USBVersionSubminor() const { return mUSBVersionSubminor; }
|
||||
uint8_t DeviceClass() const { return mDeviceClass; }
|
||||
uint8_t DeviceSubclass() const { return mDeviceSubclass; }
|
||||
uint8_t DeviceProtocol() const { return mDeviceProtocol; }
|
||||
uint8_t DeviceVersionMajor() const { return mDeviceVersionMajor; }
|
||||
uint8_t DeviceVersionMinor() const { return mDeviceVersionMinor; }
|
||||
uint8_t DeviceVersionSubminor() const { return mDeviceVersionSubminor; }
|
||||
void GetManufacturerName(Nullable<nsString>& aValue) const
|
||||
{ aValue = mManufacturerName; }
|
||||
void GetProductName(Nullable<nsString>& aValue) const
|
||||
{ aValue = mProductName; }
|
||||
void GetSerialNumber(Nullable<nsString>& aValue) const
|
||||
{ aValue = mSerialNumber; }
|
||||
bool Opened() const { return mOpened; }
|
||||
USBConfiguration* GetConfiguration() const { return mConfiguration; }
|
||||
void GetConfigurations(nsTArray<RefPtr<USBConfiguration>>& aValue) const
|
||||
{ aValue = mConfigurations; }
|
||||
|
||||
already_AddRefed<Promise> Open(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Close(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Forget(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> SelectConfiguration(uint8_t aValue,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> ClaimInterface(uint8_t aNumber, ErrorResult& aRv);
|
||||
already_AddRefed<Promise> ReleaseInterface(uint8_t aNumber,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> SelectAlternateInterface(uint8_t aNumber,
|
||||
uint8_t aSetting,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> ClearHalt(USBDirection aDirection,
|
||||
uint8_t aEndpoint, ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Reset(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> ControlTransferIn(
|
||||
const USBControlTransferParameters& aSetup, uint16_t aLength,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> ControlTransferOut(
|
||||
const USBControlTransferParameters& aSetup,
|
||||
const Optional<ArrayBufferViewOrArrayBuffer>& aData, ErrorResult& aRv);
|
||||
already_AddRefed<Promise> TransferIn(uint8_t aEndpoint, uint32_t aLength,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> TransferOut(
|
||||
uint8_t aEndpoint, const ArrayBufferViewOrArrayBuffer& aData,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(USBDevice, DOMEventTargetHelper)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
~USBDevice();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_USBDevice_h
|
||||
19
dom/usb/moz.build
Normal file
19
dom/usb/moz.build
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'USB.h',
|
||||
'USBBackend.h',
|
||||
'USBDescriptors.h',
|
||||
'USBDevice.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'USB.cpp',
|
||||
'USBBackend.cpp',
|
||||
'USBDescriptors.cpp',
|
||||
'USBDevice.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||
SOURCES += ['USBBackendWin.cpp']
|
||||
OS_LIBS += ['setupapi', 'winusb']
|
||||
|
|
@ -105,6 +105,11 @@ partial interface Navigator {
|
|||
readonly attribute Permissions permissions;
|
||||
};
|
||||
|
||||
partial interface Navigator {
|
||||
[SameObject, SecureContext, Pref="dom.usb.enabled"]
|
||||
readonly attribute USB usb;
|
||||
};
|
||||
|
||||
// Things that definitely need to be in the spec and and are not for some
|
||||
// reason. See https://www.w3.org/Bugs/Public/show_bug.cgi?id=22406
|
||||
partial interface Navigator {
|
||||
|
|
|
|||
153
dom/webidl/USB.webidl
Normal file
153
dom/webidl/USB.webidl
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
[SecureContext, Pref="dom.usb.enabled", Exposed=Window]
|
||||
interface USB : EventTarget {
|
||||
[NewObject]
|
||||
Promise<sequence<USBDevice>> getDevices();
|
||||
|
||||
[NewObject]
|
||||
Promise<USBDevice> requestDevice(optional USBDeviceRequestOptions options = {});
|
||||
|
||||
attribute EventHandler onconnect;
|
||||
attribute EventHandler ondisconnect;
|
||||
};
|
||||
|
||||
dictionary USBDeviceRequestOptions {
|
||||
required sequence<USBDeviceFilter> filters;
|
||||
sequence<USBDeviceFilter> exclusionFilters = [];
|
||||
};
|
||||
|
||||
dictionary USBDeviceFilter {
|
||||
unsigned short vendorId;
|
||||
unsigned short productId;
|
||||
octet classCode;
|
||||
octet subclassCode;
|
||||
octet protocolCode;
|
||||
DOMString serialNumber;
|
||||
};
|
||||
|
||||
enum USBDirection {
|
||||
"in",
|
||||
"out"
|
||||
};
|
||||
|
||||
enum USBEndpointType {
|
||||
"bulk",
|
||||
"interrupt",
|
||||
"isochronous"
|
||||
};
|
||||
|
||||
enum USBTransferStatus {
|
||||
"ok",
|
||||
"stall",
|
||||
"babble"
|
||||
};
|
||||
|
||||
enum USBRequestType {
|
||||
"standard",
|
||||
"class",
|
||||
"vendor"
|
||||
};
|
||||
|
||||
enum USBRecipient {
|
||||
"device",
|
||||
"interface",
|
||||
"endpoint",
|
||||
"other"
|
||||
};
|
||||
|
||||
dictionary USBControlTransferParameters {
|
||||
required USBRequestType requestType;
|
||||
required USBRecipient recipient;
|
||||
required octet request;
|
||||
required unsigned short value;
|
||||
required unsigned short index;
|
||||
};
|
||||
|
||||
[SecureContext, Pref="dom.usb.enabled", Exposed=Window]
|
||||
interface USBDevice : EventTarget {
|
||||
readonly attribute octet usbVersionMajor;
|
||||
readonly attribute octet usbVersionMinor;
|
||||
readonly attribute octet usbVersionSubminor;
|
||||
readonly attribute octet deviceClass;
|
||||
readonly attribute octet deviceSubclass;
|
||||
readonly attribute octet deviceProtocol;
|
||||
readonly attribute unsigned short vendorId;
|
||||
readonly attribute unsigned short productId;
|
||||
readonly attribute octet deviceVersionMajor;
|
||||
readonly attribute octet deviceVersionMinor;
|
||||
readonly attribute octet deviceVersionSubminor;
|
||||
readonly attribute DOMString? manufacturerName;
|
||||
readonly attribute DOMString? productName;
|
||||
readonly attribute DOMString? serialNumber;
|
||||
readonly attribute USBConfiguration? configuration;
|
||||
readonly attribute sequence<USBConfiguration> configurations;
|
||||
readonly attribute boolean opened;
|
||||
|
||||
[NewObject] Promise<void> open();
|
||||
[NewObject] Promise<void> close();
|
||||
[NewObject] Promise<void> forget();
|
||||
[NewObject] Promise<void> selectConfiguration(octet configurationValue);
|
||||
[NewObject] Promise<void> claimInterface(octet interfaceNumber);
|
||||
[NewObject] Promise<void> releaseInterface(octet interfaceNumber);
|
||||
[NewObject] Promise<void> selectAlternateInterface(octet interfaceNumber,
|
||||
octet alternateSetting);
|
||||
[NewObject] Promise<void> clearHalt(USBDirection direction,
|
||||
octet endpointNumber);
|
||||
[NewObject] Promise<void> reset();
|
||||
[NewObject] Promise<USBInTransferResult> controlTransferIn(
|
||||
USBControlTransferParameters setup, unsigned short length);
|
||||
[NewObject] Promise<USBOutTransferResult> controlTransferOut(
|
||||
USBControlTransferParameters setup, optional BufferSource data);
|
||||
[NewObject] Promise<USBInTransferResult> transferIn(octet endpointNumber,
|
||||
unsigned long length);
|
||||
[NewObject] Promise<USBOutTransferResult> transferOut(
|
||||
octet endpointNumber, BufferSource data);
|
||||
};
|
||||
|
||||
[SecureContext, Exposed=Window]
|
||||
interface USBConfiguration {
|
||||
readonly attribute octet configurationValue;
|
||||
readonly attribute DOMString? configurationName;
|
||||
readonly attribute sequence<USBInterface> interfaces;
|
||||
};
|
||||
|
||||
[SecureContext, Exposed=Window]
|
||||
interface USBInterface {
|
||||
readonly attribute octet interfaceNumber;
|
||||
readonly attribute USBAlternateInterface alternate;
|
||||
readonly attribute sequence<USBAlternateInterface> alternates;
|
||||
readonly attribute boolean claimed;
|
||||
};
|
||||
|
||||
[SecureContext, Exposed=Window]
|
||||
interface USBAlternateInterface {
|
||||
readonly attribute octet alternateSetting;
|
||||
readonly attribute octet interfaceClass;
|
||||
readonly attribute octet interfaceSubclass;
|
||||
readonly attribute octet interfaceProtocol;
|
||||
readonly attribute DOMString? interfaceName;
|
||||
readonly attribute sequence<USBEndpoint> endpoints;
|
||||
};
|
||||
|
||||
[SecureContext, Exposed=Window]
|
||||
interface USBEndpoint {
|
||||
readonly attribute octet endpointNumber;
|
||||
readonly attribute USBDirection direction;
|
||||
readonly attribute USBEndpointType type;
|
||||
readonly attribute unsigned short packetSize;
|
||||
};
|
||||
|
||||
[SecureContext, Exposed=Window]
|
||||
interface USBInTransferResult {
|
||||
readonly attribute USBTransferStatus status;
|
||||
// UXP's binding generator does not expose DataView as a WebIDL type;
|
||||
// transfer data is returned as an ArrayBuffer until that binding exists.
|
||||
readonly attribute ArrayBuffer? data;
|
||||
};
|
||||
|
||||
[SecureContext, Exposed=Window]
|
||||
interface USBOutTransferResult {
|
||||
readonly attribute USBTransferStatus status;
|
||||
readonly attribute unsigned long bytesWritten;
|
||||
};
|
||||
|
|
@ -16,6 +16,7 @@ PREPROCESSED_WEBIDL_FILES = [
|
|||
]
|
||||
|
||||
WEBIDL_FILES = [
|
||||
'USB.webidl',
|
||||
'AbortController.webidl',
|
||||
'AbortSignal.webidl',
|
||||
'AbstractWorker.webidl',
|
||||
|
|
|
|||
|
|
@ -5322,6 +5322,9 @@ pref("security.block_ftp_subresources", true);
|
|||
// Disable Storage api by default. This needs specific front-end parts to be usable.
|
||||
pref("dom.storageManager.enabled", false);
|
||||
|
||||
// Disable WebUSB by default.
|
||||
pref("dom.usb.enabled", false);
|
||||
|
||||
// DoS protection for HTTP Auth prompt spawning.
|
||||
// -1 = completely disable HTTP Auth prompting. (careful!)
|
||||
// 0 = disable this DoS protection
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue