| "use strict"; |
| const FocusEvent = require("../generated/FocusEvent.js"); |
| const idlUtils = require("../generated/utils.js"); |
| const { isDisabled } = require("./form-controls.js"); |
| const { firstChildWithLocalName } = require("./traversal"); |
| const { createAnEvent } = require("./events"); |
| const { HTML_NS, SVG_NS } = require("./namespaces"); |
| const { isRenderedElement } = require("./svg/render"); |
| |
| const focusableFormElements = new Set(["input", "select", "textarea", "button"]); |
| |
| // https://html.spec.whatwg.org/multipage/interaction.html#focusable-area, but also some of |
| // https://html.spec.whatwg.org/multipage/interaction.html#focusing-steps and some of |
| // https://svgwg.org/svg2-draft/interact.html#TermFocusable |
| exports.isFocusableAreaElement = elImpl => { |
| // We implemented most of the suggested focusable elements found here: |
| // https://html.spec.whatwg.org/multipage/interaction.html#tabindex-value |
| // However, some suggested elements are not focusable in web browsers, as detailed here: |
| // https://github.com/whatwg/html/issues/5490 |
| if (elImpl._namespaceURI === HTML_NS) { |
| if (!elImpl._ownerDocument._defaultView) { |
| return false; |
| } |
| |
| if (!elImpl.isConnected) { |
| return false; |
| } |
| |
| if (!Number.isNaN(parseInt(elImpl.getAttributeNS(null, "tabindex")))) { |
| return true; |
| } |
| |
| if (elImpl._localName === "iframe") { |
| return true; |
| } |
| |
| if (elImpl._localName === "a" && elImpl.hasAttributeNS(null, "href")) { |
| return true; |
| } |
| |
| if (elImpl._localName === "summary" && elImpl.parentNode && |
| elImpl.parentNode._localName === "details" && |
| elImpl === firstChildWithLocalName(elImpl.parentNode, "summary")) { |
| return true; |
| } |
| |
| if (focusableFormElements.has(elImpl._localName) && !isDisabled(elImpl)) { |
| if (elImpl._localName === "input" && elImpl.type === "hidden") { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| if (elImpl.hasAttributeNS(null, "contenteditable")) { |
| return true; |
| } |
| |
| return false; |
| |
| // This does not check for a designMode Document as specified in |
| // https://html.spec.whatwg.org/multipage/interaction.html#editing-host because the designMode |
| // attribute is not implemented. |
| } |
| |
| if (elImpl._namespaceURI === SVG_NS) { |
| if (!Number.isNaN(parseInt(elImpl.getAttributeNS(null, "tabindex"))) && isRenderedElement(elImpl)) { |
| return true; |
| } |
| |
| if (elImpl._localName === "a" && elImpl.hasAttributeNS(null, "href")) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| return false; |
| }; |
| |
| // https://html.spec.whatwg.org/multipage/interaction.html#fire-a-focus-event plus the steps of |
| // https://html.spec.whatwg.org/multipage/interaction.html#focus-update-steps that adjust Documents to Windows |
| // It's extended with the bubbles option to also handle focusin/focusout, which are "defined" in |
| // https://w3c.github.io/uievents/#event-type-focusin. See https://github.com/whatwg/html/issues/3514. |
| exports.fireFocusEventWithTargetAdjustment = (name, target, relatedTarget, { bubbles = false } = {}) => { |
| if (target === null) { |
| // E.g. firing blur with nothing previously focused. |
| return; |
| } |
| |
| const event = createAnEvent(name, target._globalObject, FocusEvent, { |
| bubbles, |
| composed: true, |
| relatedTarget, |
| view: target._ownerDocument._defaultView, |
| detail: 0 |
| }); |
| |
| if (target._defaultView) { |
| target = idlUtils.implForWrapper(target._defaultView); |
| } |
| |
| target._dispatch(event); |
| }; |