Skip to content

Commit

Permalink
Further fixes to focus delegation
Browse files Browse the repository at this point in the history
This ensures that focus delegation, used by both delegatesFocus and <dialog>, does not traverse into non-delegatesFocus shadow trees. Otherwise you would get unintuitive results in situations such as the following:

<div id="outer">
<#shadowroot delegatesFocus=true>
  <div id="inner>
  <#shadowroot delegatesFocus=false>
    <input>
  </#shadowroot>
  </div>
</#shadowroot>
</div>

In such a scenario, with the previous specification, inner.focus() would not focus anything, since inner's shadow root does not delegate focus so we never hit the "focus delegate" algorithm. But outer.focus() would focus the <input> element, since with it having delegatesFocus=true, we use the "focus delegate" algorithm, which would look at all shadow-including descendants. After this change, both inner.focus() and outer.focus() would not focus anything, since the <input> is hidden inside a delegatesFocus=false shadow tree.

Discovered in https://matrixlogs.bakkot.com/WHATWG/2021-11-23#L36-L62.
  • Loading branch information
domenic authored Jan 31, 2022
1 parent 4d93c06 commit d1fca22
Showing 1 changed file with 35 additions and 19 deletions.
54 changes: 35 additions & 19 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -2993,7 +2993,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li>The <dfn data-x-href="https://dom.spec.whatwg.org/#dom-shadowroot-slot-assignment">slot assignment</dfn> concept</li>
<li>The <dfn data-x-href="https://dom.spec.whatwg.org/#concept-slotable">slottable</dfn> concept</li>
<li>The <dfn data-x-href="https://dom.spec.whatwg.org/#assign-slotables-for-a-tree">assign slottables for a tree</dfn> algorithm</li>
<li>The <dfn data-x-href="https://dom.spec.whatwg.org/#concept-tree-inclusive-descendant">inclusive descendants</dfn> concept</li>
<li>The <dfn data-x-href="https://dom.spec.whatwg.org/#concept-tree-inclusive-descendant">inclusive descendant</dfn> concept</li>
<li>The <dfn data-x="finding flattened slottables" data-x-href="https://dom.spec.whatwg.org/#find-flattened-slotables">find flattened slottables</dfn> algorithm</li>
<li>The <dfn data-x-href="https://dom.spec.whatwg.org/#slottable-manual-slot-assignment">manual slot assignment</dfn> concept</li>
<li>The <dfn data-x-href="https://dom.spec.whatwg.org/#assign-a-slot">assign a slot</dfn> algorithm</li>
Expand Down Expand Up @@ -75823,14 +75823,11 @@ END:VCARD</pre>

<dd>
<ol>
<li><p>Let <var>root</var> be <var>focus target</var>'s <span
data-x="concept-element-shadow-root">shadow root</span>.</p></li>

<li><p>If <var>root</var> is a <span>shadow-including inclusive ancestor</span> of the
<li><p>If <var>focus target</var> is a <span>shadow-including inclusive ancestor</span> of the
<span>currently focused area of a top-level browsing context</span>'s <span>DOM anchor</span>,
then return null.</p></li>

<li><p>Return the <span>focus delegate</span> for <var>root</var> given <var>focus
<li><p>Return the <span>focus delegate</span> for <var>focus target</var> given <var>focus
trigger</var>.</p></li>
</ol>

Expand All @@ -75851,29 +75848,48 @@ END:VCARD</pre>
steps:</p>

<ol>
<li><p>Let <var>autofocus delegate</var> be the <span>autofocus delegate</span> for
<var>focus target</var> given <var>focus trigger</var>.</p></li>
<li><p>Let <var>autofocus delegate</var> be the <span>autofocus delegate</span> for <var>focus
target</var> given <var>focus trigger</var>.</p></li>

<li><p>If <var>autofocus delegate</var> is not null, then return <var>autofocus
delegate</var>.</p></li>

<li><p>If <var>focus trigger</var> is "<code data-x="">click</code>", then let <var>possible
focus delegates</var> be the list of all <span>click focusable</span> <span data-x="focusable
area">focusable areas</span> whose <span>DOM anchor</span> is a <span>shadow-including
descendant</span> of <var>focus target</var>.</p></li>
<li><p>If <var>focus target</var> is a <span>shadow host</span> and its <span
data-x="concept-element-shadow-root">shadow root</span>'s <span>delegates focus</span> is false,
then return null.</p></li>

<li><p>Otherwise, let <var>possible focus delegates</var> be the list of all <span
data-x="focusable area">focusable areas</span> whose <span>DOM anchor</span> is a
<span>shadow-including descendant</span> of <var>focus target</var>.</p></li>
<li><p>Let <var>parent</var> be <var>focus target</var>.</p></li>

<li><p>If <var>possible focus delegates</var> is empty, then return null.</p></li>
<li><p>If <var>parent</var> is a <span>shadow host</span>, then set <var>parent</var> to
<var>parent</var>'s <span data-x="concept-element-shadow-root">shadow root</span>.</p></li>

<li><p>Sort <var>possible focus delegates</var> by <span>shadow-including tree order</span> of
their <span data-x="DOM anchor">DOM anchors</span>.</p></li>
<li>
<p><span data-x="list iterate">For each</span> <var>child</var> of <var>parent</var>'s <span
data-x="concept-tree-child">children</span>:</p>

<li><p>Return <var>possible focus delegates</var>[0].</p></li>
<ol>
<li>
<p>Let <var>focusable area</var> be <var>child</var>, if <var>child</var> is a
<span>focusable area</span>; otherwise let <var>focusable area</var> be the result of <span
data-x="get the focusable area">getting the focusable area</span> for <var>child</var> given
<var>focus trigger</var>.</p>

<p class="note">This step can end up recursing, i.e., the <span>get the focusable area</span>
steps might return the <span>focus delegate</span> of <var>child</var>.</p>
</li>

<li><p>If <var>focusable area</var> is not null, then return <var>focusable
area</var>.</p></li>
</ol>
</li>

<li><p>Return null.</p></li>
</ol>

<p class="note">The above algorithm essentially returns the first suitable <span>focusable
area</span> where the path between its <span>DOM anchor</span> and <var>focus target</var>
delegates focus at any shadow tree boundaries.</p>

<p>The <dfn>autofocus delegate</dfn> for a <var>focus target</var> given a <var>focus
trigger</var> is given by the following steps:</p>

Expand Down

0 comments on commit d1fca22

Please sign in to comment.