Repository: adobe/react-spectrum
File: packages/@react-aria/focus/src/FocusScope.tsx (TypeScript)

Code before:
}

function getScopeRoot(scope: Element[]) {
  return scope[0].parentElement!;
}

function shouldContainFocus(scopeRef: ScopeRef) {
  let scope = focusScopeTree.getTreeNode(activeScope);
  while (scope && scope.scopeRef !== scopeRef) {
    if (scope.contain) {
      return false;
    }

    scope = scope.parent;
  }

  return true;
}

function isTabbableRadio(element: HTMLInputElement) {
  if (element.checked) {
    return true;
  }
  let radios: HTMLInputElement[] = [];
  if (!element.form) {
    radios = ([...getOwnerDocument(element).querySelectorAll(`input[type="radio"][name="${element.name}"]`)] as HTMLInputElement[]).filter(radio => !radio.form);
  } else {
    let radioList = element.form?.elements?.namedItem(element.name) as RadioNodeList;
    radios = [...(radioList ?? [])] as HTMLInputElement[];
  }
  if (!radios) {
    return false;
  }
  let anyChecked = radios.some(radio => radio.checked);

  return !anyChecked;
}

function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: boolean) {
  let focusedNode = useRef<FocusableElement>(undefined);

  let raf = useRef<ReturnType<typeof requestAnimationFrame>>(undefined);
  useLayoutEffect(() => {
    let scope = scopeRef.current;
    if (!contain) {
      // if contain was changed, then we should cancel any ongoing waits to pull focus back into containment
      if (raf.current) {
        cancelAnimationFrame(raf.current);
        raf.current = undefined;
      }
      return;

Review comment (security):
Might need to escape the name here?
```suggestion
    radios = ([...getOwnerDocument(element).querySelectorAll(`input[type="radio"][name="${CSS.escape(element.name)}"]`)] as HTMLInputElement[]).filter(radio => !radio.form);
```

Code after:
}

function getScopeRoot(scope: Element[]) {
  return scope[0].parentElement!;
}

function shouldContainFocus(scopeRef: ScopeRef) {
  let scope = focusScopeTree.getTreeNode(activeScope);
  while (scope && scope.scopeRef !== scopeRef) {
    if (scope.contain) {
      return false;
    }

    scope = scope.parent;
  }

  return true;
}

function isTabbableRadio(element: HTMLInputElement) {
  if (element.checked) {
    return true;
  }
  let radios: HTMLInputElement[] = [];
  if (!element.form) {
    radios = ([...getOwnerDocument(element).querySelectorAll(`input[type="radio"][name="${CSS.escape(element.name)}"]`)] as HTMLInputElement[]).filter(radio => !radio.form);
  } else {
    let radioList = element.form?.elements?.namedItem(element.name) as RadioNodeList;
    radios = [...(radioList ?? [])] as HTMLInputElement[];
  }
  if (!radios) {
    return false;
  }
  let anyChecked = radios.some(radio => radio.checked);

  return !anyChecked;
}

function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: boolean) {
  let focusedNode = useRef<FocusableElement>(undefined);

  let raf = useRef<ReturnType<typeof requestAnimationFrame>>(undefined);
  useLayoutEffect(() => {
    let scope = scopeRef.current;
    if (!contain) {
      // if contain was changed, then we should cancel any ongoing waits to pull focus back into containment
      if (raf.current) {
        cancelAnimationFrame(raf.current);
        raf.current = undefined;
      }
      return;