whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.09k stars 2.66k forks source link

Explicit display:none should prevent element from being used as relevant canvas fallback content #7534

Open Loirooriol opened 2 years ago

Loirooriol commented 2 years ago

https://html.spec.whatwg.org/multipage/canvas.html#being-used-as-relevant-canvas-fallback-content

An element whose nearest canvas element ancestor is being rendered and represents embedded content is an element that is being used as relevant canvas fallback content.

https://html.spec.whatwg.org/multipage/interaction.html#focusable-area

Focusable area Examples Elements that meet all the following criteria:

  • the element's tabindex value is non-null, or the element is determined by the user agent to be focusable;
  • the element is either not a shadow host, or has a shadow root whose delegates focus is false;
  • the element is not actually disabled;
  • the element is not expressly inert;
  • the element is either being rendered or being used as relevant canvas fallback content.

But let's say you have multiple fallbacks and choose between them with display:none:

<canvas>
  <div id="fallback1" style="display:none">
    <span tabindex="1">Fallback1</span>
  </div>
  <div id="fallback2">
    <span tabindex="1">Fallback2</span>
  </div>
</cavas>

As @lilles told me, in the case where we should render the fallback content, #fallback1>span would not be focusable. It sounds unintentional that all of the fallback content should be focusable if we render the canvas and not render the fallback.

Also, it's already not focusable in Firefox, and I plan to make it not focusable in Blink, since it helps for inert. The spec lists "not expressly inert" as a condition for being focusable, but typically browsers don't compute styles in display: none subtrees. But all Blink, WebKit and Gecko have converged to track inertness in the computed style, so it may not be known if the element is inert or not.

Loirooriol commented 2 years ago

Same for display: contents and not being in the flat tree.

annevk commented 2 years ago

That seems reasonable, but I'm wondering how this works technically. Do you essentially try to render the fallback contents to determine what would and would not render?

This seems related to #7490.

cc @whatwg/a11y @whatwg/canvas

patrickhlauke commented 2 years ago

naive question: would this then hinge not on "is being rendered" but on a more abstract "has been flagged as not rendered/renderable through the use of display:none or similar" (i.e. is there some kind of "state" or flag that comes before the browser's decision to render or not render something, and if so should it lean on that rather than "is being rendered" which is the outcome of that evaluation)

Loirooriol commented 2 years ago

Do you essentially try to render the fallback contents to determine what would and would not render?

No. In Blink, elements in display: none typically don't have a cached ComputedStyle, unless e.g. a script uses getComputedStyle or something to force it. So what I did is: if the element doesn't have a cached computed style, or if it has the IsEnsuredInDisplayNone() flag, or if has display: contents, then it's not focusable.

Note this is an approximation, since e.g. in the canvas fallback content there could be a replaced element with a child:

canvas.innerHTML = "<img src='image'>";
var div = document.createElement("div");
div.tabIndex = -1;
canvas.firstElementChild.append(div);
div.focus();

Even when rendering the canvas fallback content, the div wouldn't be rendered, but it's still focusable, even if it wouldn't be focusable outside of a canvas.

naive question: would this then hinge not on "is being rendered" but on a more abstract "has been flagged as not rendered/renderable through the use of display:none or similar"

That's the case when inside a canvas. Otherwise Blink uses "is being rendered". It may be possible to have a more consistent behavior.

annevk commented 2 years ago

Interesting, I would have expected fallback content to be treated similarly to display:none as it cannot appear on screen. I guess we can define something along the lines you suggest.

cc @whatwg/css @emilio

Loirooriol commented 2 years ago

Treating it like display: none would make canvas fallback content not focusable at all. Which would be another possibility, but seems a riskier change. I basically did the minimal change to consistently handle inertness (it's tracked in the computed style, so it's a problem if elements in display: none that don't have a cached computed style can be focused).

junov commented 2 years ago

Se the discussion on this other closely related issue (already mentioned above). We may want to revisit the interactiveness of fallback content.