Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

navigator.hid.addEventListener('connect', async ({device})) called to early #65

Open
Raketten1963 opened this issue Jun 5, 2021 · 5 comments

Comments

@Raketten1963
Copy link

Lately the timing in when the "connect" event is called has changed and I found that the device.collections is not yet fully populated. The array is typically only of length 1, while if you log the device to console, you will see the right length, which in my case is 3...

It did not use to be like this, but I see it now in 91.0.4472.77 and even in latest canary build...

@nondebug
Copy link
Collaborator

nondebug commented Jun 7, 2021

Is this on Windows?

The Windows platform HID API enumerates each top-level HID collection as a separate device. In Chrome 88 and earlier, WebHID would enumerate these as separate HIDDevice objects but in Chrome 89 and later it exposes a single HIDDevice object for all collections that are part of the same HID interface.

When Chrome performs its initial device enumeration, it should be able to find all collections from every connected HID device. However, when devices are hotplugged it's possible for some of the HID collections to be enumerated earlier than others. Ideally Chrome should determine how many HID collections are expected and delay firing 'connect' events until all collections have been enumerated. This information isn't available on Windows, so Chrome fires 'connect' when the first collection is enumerated and updates the collection info (without firing an event) when other collections are found.

It sounds like it may be useful to add a 'devicechange' event that would fire on collection info changes.

@Raketten1963
Copy link
Author

Spot on Matt, it is windows and your solution sounds great...

@trameshJabra
Copy link

On Windows we can register for notification on each of the top level HID collections it exposes and also on a HID class device. Applications will get device removal notification for all the HID collections as well as a final notification for the HID device itself. Since Chrome now exposes a single HID object I think it makes sense to just have one device added notification when the device is fully enumerated and a single device removal when it is unplugged.

@nondebug
Copy link
Collaborator

when the device is fully enumerated

If it's possible to know when all exposed HID collections are enumerated then I agree, that would be preferred over modifying the API to add a devicechange event. The current Chromium implementation works the way it does because I couldn't find a way to detect the "fully enumerated" state. Do you know of anything that would help detect when a device is fully enumerated?

I think it's meant to be impossible. Windows can't guarantee that it will enumerate all of the collections described in the report descriptor because some of them might be protected. Applications should not be able to learn about protected collections or reports. WebHID tries to make similar guarantees.

I'm still not really sure what's causing this issue and haven't been able to reproduce it locally. I suspect it has to do with installed vendor drivers introducing some delay in the enumeration step. If it's true that vendor drivers can introduce arbitrarily long delays before collections are enumerated then I don't see any scenario where we can wait for all collections to be enumerated before firing the connect event. The driver might decide not to enumerate the collection at all, causing the whole device to be unusable.

I suspect it's possible for drivers to toggle visibility for individual HID collections which suggests we should fire devicechange when a collection is removed as well as when it's added. I think drivers like HIDGuardian can do this but I haven't tested to see how it interacts with the Chromium implementation.

@egm0121
Copy link

egm0121 commented Oct 14, 2024

Hi @nondebug,

With the last comment on this thread being from the start of 2022, I'd like to know the stage of this devicechange event proposal.
It is important to have a way to discover when a new HID collection is added/removed on the HIDDevice.collections array on Windows.

The issue is still present in Chrome 129, I can reproduce it intermittently but quite easily on a Windows laptop (Intel(R) Core(TM) i5-7300U CPU @ 2.60GHz 16GB RAM) by plugging in a wired headset eg. (Jabra Evolve Link or Sennheiser sc60) and looking at the collections array over time after the connect event using the following snippet:

navigator.hid.addEventListener('connect', async e => {
  console.log('**connected device:', e.device.productName, 'inserted collections:', e.device.collections?.map(c => ({
    usage: c.usagePage,
    vendorId: e.device.vendorId
  })));
  await new Promise(res => setTimeout(res, 500));
  console.log(
    '** after 0.5 sec collections:',
    e.device.collections?.map(c => ({
      usage: c.usagePage,
      vendorId: e.device.vendorId
    }))
  );
  await new Promise(res => setTimeout(res, 500));
  console.log(
    '** after 1 sec collections:',
    e.device.collections?.map(c => ({
      usage: c.usagePage,
      vendorId: e.device.vendorId
    }))
  );
  await new Promise(res => setTimeout(res, 1e3));
  console.log(
    '** after 2 sec collections:',
    e.device.collections?.map(c => ({
      usage: c.usagePage,
      vendorId: e.device.vendorId
    }))
  );
});

output:

Screenshot 2024-10-10 at 3 38 40 PM

The current workaround involves waiting an arbitrary amount of time before checking the HIDDevice.collections array to ensure there is enough time on Windows to access all enumerable collections. but it is not a complete solution and it adds unneeded latency to the flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants