-
Notifications
You must be signed in to change notification settings - Fork 36
Add focusing changes + monkeypatches #68
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
Changes from all commits
bb0a18c
8bd6ee1
126aff2
55400fa
a06b792
5521bfd
bb73af9
2acc003
17e4cb3
fbb1d4c
2e331e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,9 @@ spec: prerendering-revamped; urlPrefix: https://wicg.github.io/nav-speculation/p | |
| for: navigable | ||
| text: loading mode; url: #navigable-loading-mode | ||
|
|
||
| spec: fetch; urlPrefix: https://fetch.spec.whatwg.org/ | ||
| type: dfn | ||
| text: queue a cross-origin embedder policy CORP violation report; url: queue-a-cross-origin-embedder-policy-corp-violation-report | ||
| spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/ | ||
| type: dfn | ||
| urlPrefix: browsers.html | ||
|
|
@@ -86,12 +89,36 @@ spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/ | |
| urlPrefix: interaction.html | ||
| text: activation notification; url: activation-notification | ||
| text: consume user activation; url: consume-user-activation | ||
| text: activation; url: activation | ||
| text: click focusable; url: click-focusable | ||
| text: focusable area; url: focusable-area | ||
| text: sequential focus navigation; url: sequential-focus-navigation | ||
| text: focus; url: dom-window-focus | ||
| text: focus chain; url: focus-chain | ||
| text: focus update steps; url: focus-update-steps | ||
| text: focused; url: focused | ||
| text: gain focus; url: gains-focus | ||
| text: DOM anchor; url: dom-anchor | ||
| text: get the focusable area; url: get-the-focusable-area | ||
| text: currently focused area of a top-level traversable; url: currently-focused-area-of-a-top-level-traversable | ||
| text: focused area; url: focused-area-of-the-document | ||
| text: sequential navigation search algorithm; url: sequential-navigation-search-algorithm | ||
| urlPefix: infrastructure.html | ||
| text: immediately; url: immediately | ||
| urlPrefix: nav-history-apis.html | ||
| for: Window | ||
| text: navigable; url: window-navigable | ||
| spec: fetch; urlPrefix: https://fetch.spec.whatwg.org/ | ||
| type: dfn | ||
| text: queue a cross-origin embedder policy CORP violation report; url: queue-a-cross-origin-embedder-policy-corp-violation-report | ||
| urlPrefix: interactive-elements.html | ||
| text: accesskey attribute command; url: using-the-accesskey-attribute-to-define-a-command-on-other-elements | ||
| text: previously focused element; url: previously-focused-element | ||
| urlPrefix: popover.html | ||
| text: hide popover algorithm; url: hide-popover-algorithm | ||
| urlPrefix: form-control-infrastructure.html | ||
| text: interactively validate the constraints; url: interactively-validate-the-constraints | ||
| urlPrefix: custom-elements.html | ||
| text: face validation anchor; url: face-validation-anchor | ||
| urlPrefix: webappapis.html | ||
| text: fire a click event; url: fire-a-click-event | ||
| spec: RFC8941; urlPrefix: https://www.rfc-editor.org/rfc/rfc8941.html | ||
| type: dfn | ||
| text: structured header; url: #section-1 | ||
|
|
@@ -759,6 +786,185 @@ in the [[#nested-traversables-intro]]. | |
| 1. Return |navigables|. | ||
| </div> | ||
|
|
||
| <h3 id=focusing-changes>Modifications to the focusing algorithms</h3> | ||
|
|
||
| The [[HTML]] standard defines how to handle focusing elements and {{Window}}s, both by user gesture | ||
| and through script-initiated APIs. Since fenced frames are designed to prevent communication across | ||
| a fenced frame boundary, we need to handle focusing carefully. This is because when focus is pulled | ||
| across a fenced frame boundary, contexts on both sides of the boundary can detect that change, which | ||
| can be used to open a communication channel between a <{fencedframe}> and its embedder. | ||
|
|
||
| We do this is by not allowing the [=focusing steps=] to move script-initiated focus across a fenced | ||
| frame boundary. | ||
|
|
||
| <p class=example id=user-initiated-focus>When a user clicks on an element like a <{button}> inside a | ||
| <{fencedframe}> while another element *outside* of the <{fencedframe}> is [=focused=], because this | ||
| is a user-initiated action, the [=focusing steps=] will allow the <{button}> to [=gain focus=]. | ||
| Without allowing that, no element inside of a <{fencedframe}> would never be able [=gain | ||
| focus=].</p> | ||
|
|
||
| <p class=example id=script-initiated-focus>If we were to continue blindly allowing all elements to | ||
| be [=focused=] via the {{HTMLOrSVGElement/focus()}} method as is the status quo before this | ||
| specification, a [=fenced navigable container=] and its [=fenced navigable container/fenced | ||
| navigable=] could use a sequence of {{HTMLOrSVGElement/focus()}} calls to send arbitrary data across | ||
| the fenced frame boundary, which is a privacy leak. To avoid this, we effectively "fence" the | ||
| {{HTMLOrSVGElement/focus()}} method, which sacrifices some functionality for privacy.</p> | ||
|
|
||
| <div algorithm=focusing-steps-patch> | ||
| Modify the [=focusing steps=] to take a new optional [=boolean=] argument <dfn | ||
| lt="focus-unfenced">unfenced</dfn> that defaults to false. | ||
|
|
||
| Add a new step after step 3 of the algorithm (that changes new focus target) that reads: | ||
|
|
||
| 4. If |new focus target| is a [=fenced navigable container=] with non-null | ||
| [=fenced navigable container/fenced navigable=], then set |new focus target| to the | ||
| [=fenced navigable container/fenced navigable=]'s [=navigable/active document=]. | ||
|
|
||
| Add a new step after the step that defines the |new chain| variable, that reads: | ||
|
|
||
| 9. If [=focus-unfenced|unfenced=] is false, |new chain| [=list/contains=] a {{Document}} | ||
| |document| whose [=node navigable=]'s [=navigable/traversable navigable=] is a | ||
| [=fenced navigable container/fenced navigable=], and <var ignore>old chain</var> does not also | ||
| [=list/contain=] |document|, then return. | ||
|
|
||
| Note: This is how we bail-out early just before calling the [=focus update steps=], in the case | ||
| where focus is trying to cross the fence. | ||
|
|
||
| Modify the user agent sentence after the algorithm steps in [=focusing steps=] to read: | ||
|
|
||
| User agents must [=immediately=] run the [=focusing steps=] for a [=focusable area=] or | ||
| [=navigable=] |candidate| with [=focus-unfenced|unfenced=] set to true whenever the user attempts to | ||
| move the focus to |candidate|. | ||
| </div> | ||
|
|
||
| <div algorithm=access-key-patch> | ||
| Modify the action of the [=accesskey attribute command=] algorithm to be: | ||
|
|
||
| 1. Run the [=focusing steps=] for the element with [=focus-unfenced|unfenced=] set to true. | ||
|
|
||
| 1. <a>Fire a `click` event</a> at the element. | ||
| </div> | ||
|
|
||
| <div algorithm=activation-patch> | ||
| Modify the behavior when a user [=activation|activates=] a [=click focusable=] [=focusable area=] | ||
| to be: | ||
|
|
||
| When a user [=activation|activates=] a [=click focusable=] [=focusable area=], the user agent must | ||
| run the [=focusing steps=] on the [=focusable area=] with <var ignore>focus trigger</var> set | ||
| to "`click`" and [=focus-unfenced|unfenced=] set to true. | ||
| </div> | ||
|
|
||
| <div algorithm=hide-popover-patch> | ||
| Modify step 10 of the [=hide popover algorithm=] to read: | ||
|
|
||
| 10. If |previouslyFocusedElement| is not null, then: | ||
| 1. Set <var ignore>element</var>'s [=previously focused element=] to null. | ||
| 2. If <var ignore>focusPreviousElement</var> is true, then run the [=focusing steps=] for | ||
| |previouslyFocusedElement| with [=focus-unfenced|unfenced=] set to true; the viewport | ||
| should not be scrolled by doing this step. | ||
|
|
||
| Note: Although dismissing a popover manually is a user-initiated gesture, the | ||
| [=focusing steps=] will be called with [=focus-unfenced|unfenced=] set to false regardless | ||
| of whether this was called from user gesture or via a script call. | ||
| </div> | ||
|
|
||
| <div algorithm=interactively-validate-patch> | ||
| Modify the first bullet point of step 3 of the [=interactively validate the constraints=] | ||
| algorithm to read: | ||
|
|
||
| * User agents may focus one of those elements in the process, by running the | ||
| [=focusing steps=] for that element, and may change the scrolling position of the | ||
| document, or perform some other action that brings the element to the user's attention. | ||
| If these steps were invoked by user gesture, [=focusing steps=] can be called with | ||
| [=focus-unfenced|unfenced=] set to true. For elements that are | ||
| [=form-associated custom elements=], user agents should use their [=face validation anchor=] | ||
| instead, for the purposes of these actions. | ||
| </div> | ||
|
|
||
| <div algorithm=has-focus-steps> | ||
| Add a step after step 2 of the while loop in the [=has focus steps=] algorithm that reads: | ||
|
|
||
| 3. If the [=focused area=] of |candidate| is a [=fenced navigable container=] with a non-null | ||
| [=fenced navigable container/fenced navigable=], then set |candidate| to the [=navigable/active | ||
| document=] of that [=fenced navigable container=]'s [=fenced navigable container/fenced | ||
| navigable=]. | ||
| </div> | ||
|
|
||
| <div algorithm=focus-chain> | ||
| Modify step 3 of the while loop in the [=focus chain=] algorithm to read: | ||
| 3. If |currentObject| is a [=focusable area=], then set |currentObject| to |currentObject|'s [=DOM | ||
| anchor=]'s [=Node/node document=]. | ||
|
|
||
| Otherwise, if |currentObject| is a {{Document}} whose [=node navigable=]'s [=navigable/parent=] | ||
| is non-null, then set |currentObject| to |currentObject|'s [=node navigable=]'s | ||
| [=navigable/parent=]. | ||
|
|
||
| Otherwise, if |currentObject| is a {{Document}} whose [=node navigable=] is a [=traversable | ||
| navigable=] whose [=traversable navigable/unfenced parent=] is non-null, then set | ||
| |currentObject| to |currentObject|'s [=node navigable=]'s [=traversable navigable/unfenced | ||
| parent=]. | ||
|
|
||
| Otherwise, [=iteration/break=]. | ||
| </div> | ||
|
|
||
| <div algorithm=get-the-focusable-area> | ||
| Modify the [=get the focusable area=] algorithm. Add a new case to the switch statement: | ||
|
|
||
| <dl class="switch"> | ||
| <dt>If <var ignore>focus target</var> is a [=fenced navigable container=] with a non-null [=fenced navigable container/fenced navigable=]</dt> | ||
|
|
||
| <dd><p>Return the [=fenced navigable container=]'s [=fenced navigable container/fenced | ||
| navigable=]'s [=navigable/active document=].</p></dd> | ||
| </dl> | ||
|
|
||
| Note: This algorithm can unconditionally "jump the fence" boundary because its return value always | ||
| feeds into an algorithm that _does_ carefully consider the fence boundary. | ||
| </div> | ||
|
|
||
| <div algorithm=currently-focused-area-of-a-top-level-traversable> | ||
| Modify step 3 of the [=currently focused area of a top-level traversable=] algorithm to read: | ||
|
|
||
| 3. While |candidate|'s [=focused area=] is either a [=navigable container=] with a non-null | ||
| [=navigable container/content navigable=] or a [=fenced navigable container=] with a non-null | ||
| [=fenced navigable container/fenced navigable=]: set |candidate| to the | ||
| [=navigable/active document=] of either that [=navigable container=]'s | ||
| [=navigable container/content navigable=] or that [=fenced navigable container=]'s | ||
| [=fenced navigable container/fenced navigable=], whichever is non-null. | ||
| </div> | ||
|
|
||
| <div algorithm=sequential-focus-navigation-patch> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All of the sequential focus and navigation changes are unfenced by default it seems. I just want to double check: that's because there are only initiated with user actions, right? I think I'm going to land this right not but please respond here after just for posterity
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| Modify step 6 of the [=sequential focus navigation=] algorithm to read: | ||
|
|
||
| 6. If |candidate| is not null, then run the [=focusing steps=] for | ||
| |candidate| with [=focus-unfenced|unfenced=] set to true and return. | ||
|
|
||
| Modify step 9 of the [=sequential focus navigation=] algorithm to read: | ||
|
|
||
| 9. Otherwise, |starting point| is a [=focusable area=] in a [=child navigable=] or | ||
| [=fenced navigable container/fenced navigable=]. Set |starting point| to that | ||
| [=child navigable=] or [=fenced navigable container/fenced navigable=]'s | ||
| [=traversable navigable/unfenced parent=] and return to the step labeled <i>loop</i>. | ||
| </div> | ||
|
|
||
| <div algorithm=sequential-navigation-search-patch> | ||
| Modify step 2 of the [=sequential navigation search algorithm=] to read: | ||
|
|
||
| 2. If |candidate| is a [=navigable container=] with a non-null | ||
| [=navigable container/content navigable=], then let |new candidate| be the result of running the | ||
| [=sequential navigation search algorithm=] with |candidate|'s | ||
| [=navigable container/content navigable=] as the first argument, <var ignore>direction</var> as | ||
| the second, and <i>sequential</i> as the third. | ||
|
|
||
| If |candidate| is a [=fenced navigable container=] with a non-null | ||
| [=fenced navigable container/fenced navigable=], then let |new candidate| be the result of | ||
| running the [=sequential navigation search algorithm=] with |candidate|'s | ||
| [=fenced navigable container/fenced navigable=] as the first argument, | ||
| <var ignore>direction</var> as the second, and <i>sequential</i> as the third. | ||
|
|
||
| If |new candidate| is null, then let <var ignore>starting point</var> | ||
| be |candidate|, and return to the top of this algorithm. Otherwise, let | ||
| |candidate| be |new candidate|. | ||
| </div> | ||
|
|
||
| <h3 id=navigation-patch>Navigation</h3> | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.