fluid-lab / gamepad-navigator

GSoC 2020 project
Other
6 stars 10 forks source link

Cannot focus on elements inside (some) shadow containers and `<iframe>` elements. #131

Closed duhrer closed 5 months ago

duhrer commented 6 months ago

In recent testing of the gamepad navigator with a page from the FLOE project, I discovered a range of unreachable elements:

  1. Elements in "closed" shadow roots (such as video elements).
  2. Elements in iframes (such as UIO on that page).

I was able to special-case video elements (see #132), but we still don't have an approach for closed shadow roots or <iframe> elements.

Given the CORS concerns and sandboxing in modern browsers, I'm not sure we're going to be able to address the concerns with <iframe> elements.

Pretty sure we won't be able to work with a "closed" shadow root either.

duhrer commented 6 months ago

NPM helpfully provides this comparison of similar libraries.

I also searched npm, and found tabbable, which seems well maintained.

duhrer commented 6 months ago

So, I forked ally and applied the shadow fix in this pull request. It still doesn't work, so there seems to be something else going on. I am explicitly testing with the same FLOE page.

Looking at which "tabbables" ally detects, it detects the video itself as tabbable, but not the sub-elements. I need to see what happens in code when I'm either right before or right after the video element. Onscreen, it just seems to wrap around. It may be that there's no focus highlight on the video itself, and that there are no sub-elements found to focus on.

I also notice that on that page, I can't reach the UIO controls (other than the "show/hide" button), I need to understand that as well. With keyboard navigation, it all "just works", there may be a few issues to unpack.

I suspect I'll end up making my own detection algorithm, but for now I need to start by picking apart the problems on a test page. I'll make a manual test fixture for now, including:

  1. An "open" shadow root with tabbables inside.
  2. A "closed" shadow root with tabbables inside.
  3. A video element.

Once I understand what's wrong, I can convert that page to a proper test fixture and use it to test either a library or our own approach.

duhrer commented 6 months ago

I constructed a test fixture as described above. With a keyboard and tab/shift+tab, I can reach:

  1. Non-shadow content in the body of the document
  2. Content within an "open" shadow root
  3. Content within a "closed" shadow root
  4. A video element that has controls
  5. The controls within that element
  6. Content within an <iframe>

With tab / shift + tab, I cannot reach:

  1. A video element with no controls

As a side note, you can right click a video element without controls, show the controls, and then tab to it. This suggests that the display of controls is something the user is allowed to change, so perhaps we can automatically enable controls for any video elements we find (as a configurable preference).

Let's now compare tab navigation with ally's detection of "tabbables", which finds the following on the same page:

  1. Non-shadow content in the body of the document
  2. Content within an "open" shadow root
  3. The outer container of a video element with controls

It does not detect:

  1. Content within a "closed" shadow root
  2. The controls within a video element with controls
  3. Content within an <iframe>

The test fixture now clearly demonstrates the two problems observed on the FlOE page.

My next step is to see what's visible in the DOM, for:

  1. The <video> element with controls
  2. The <video> element without controls
  3. The <iframe> element
  4. The "closed" shadow root container

I should also see if ally's "focusable" detection (or any of their other modes) does any better.

duhrer commented 6 months ago

First, the details of what ally detects. With the "quick" strategy, ally detects:

  1. Non-shadow content in the body of the document
  2. The outer container of the video element with controls

With the "strict" strategy, ally detects:

  1. Non-shadow content in the body of the document
  2. The outer container of the video element with controls
  3. Content in an "open" shadow root

With the "all" strategy, ally detects:

  1. Non-shadow content in the body of the document
  2. The outer container of the video element with controls
  3. Content in an "open" shadow root (including all of our hidden modal buttons!)

Although I suspected as much from previous manual testing, it's good to confirm and document that none of ally's strategy for detecting tabbables does what we need.

duhrer commented 6 months ago

Ally's focusable method detects the following using the "quick" strategy:

  1. Non-shadow content in the body of the document
  2. The outer container of the <video> element with controls
  3. The outer container of the <iframe>

Their "strict" strategy detects:

  1. Non-shadow content in the body of the document
  2. The outer container of the <video> element with controls
  3. The outer container of the <iframe>
  4. Content within an "open" shadow root (not including hidden content).

Their "all" strategy detects:

  1. Non-shadow content in the body of the document
  2. The outer container of the <video> element with controls
  3. The outer container of the <iframe>
  4. Content within an "open" shadow root (including hidden content!).

So, their "focusables" method doesn't do what we need either.

duhrer commented 6 months ago

In testing this, I discovered that clicking a video with our click action does nothing, which I've documented as #132.

duhrer commented 6 months ago

OK, we'll have to handle <video> elements with special-casing in #132, the "user agent" mode shadow root is totally opaque, we can't see anything.

This issue should now focus on <iframe> content and closed shadow roots.

duhrer commented 6 months ago

OK, with the fixes for #132, I'm going to leave this one open and update the summary to make it clearer where the hard limits of our current approach are.

duhrer commented 5 months ago

This should be broken out as we probably have an approach for iframes.

duhrer commented 5 months ago

I'm closing this out for now in favour of #155, as most of the video issues were addressed in #132.