chromium/third_party/blink/web_tests/accessibility/aria-owns-dynamic-changes.html

<!DOCTYPE HTML>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>

<div class="container">
    <ul id="future_parent" aria-owns="future_child"></ul>
</div>

<script>
test(function(t) {
    const axFutureParent = accessibilityController.accessibleElementById("future_parent");
    assert_equals(axFutureParent.childrenCount, 0);
    const futureParent = document.getElementById("future_parent");
    const child = document.createElement("li");
    child.id = "future_child";
    assert_equals(axFutureParent.childrenCount, 0, "before appending child");

    futureParent.parentElement.appendChild(child);
    assert_equals(axFutureParent.childrenCount, 1, "after appending child");
    assert_array_equals(futureParent.ariaOwnsElements, [child]);

    child.id = "";
    assert_equals(axFutureParent.childrenCount, 0, "after clearing id");
    assert_array_equals(futureParent.ariaOwnsElements, []);

    child.id = "future_child";
    assert_equals(axFutureParent.childrenCount, 1, "after setting id");
    assert_array_equals(futureParent.ariaOwnsElements, [child]);

    futureParent.setAttribute("aria-owns", "");
    assert_equals(axFutureParent.childrenCount, 0, "after clearing aria-owns");
    assert_array_equals(futureParent.ariaOwnsElements, []);

    futureParent.setAttribute("aria-owns", "future_child");
    assert_equals(axFutureParent.childrenCount, 1, "after setting aria-oens");
    assert_array_equals(futureParent.ariaOwnsElements, [child]);

    child.style.visibility = 'hidden';
    assert_equals(axFutureParent.childrenCount, 1, "after setting hidden");
    assert_true(axFutureParent.childAtIndex(0).isIgnored);
    assert_array_equals(futureParent.ariaOwnsElements, [child]);

    child.style.visibility = 'visible';
    assert_equals(axFutureParent.childrenCount, 1, "after setting visible");
    assert_array_equals(futureParent.ariaOwnsElements, [child]);

    child.remove();
    assert_equals(axFutureParent.childrenCount, 0);
    assert_array_equals(futureParent.ariaOwnsElements, []);
}, "Aria-owns is dynamically recomputed.");
</script>

<div class="container">
  <div id="explicitParent"></div>
</div>

<script>
test(function(t) {
  const axParent = accessibilityController.accessibleElementById("explicitParent");
  const child = document.createElement("li");

  // Check element reflection before inserting into the DOM.
  explicitParent.ariaOwnsElements = [child];

  assert_array_equals(explicitParent.ariaOwnsElements, [], "Element is not reflected as was not in valid scope on insertion.");
  assert_equals(explicitParent.getAttribute("aria-owns"), "", "Content attribute should be set to empty string due to invalid element.");
  assert_equals(axParent.childrenCount, 0, "Accessible node should have no children.");


  // Add the element into the DOM, and set the IDL attribute.
  explicitParent.parentElement.appendChild(child);
  explicitParent.ariaOwnsElements = [child];

  assert_array_equals(explicitParent.ariaOwnsElements, [child], "Element reference is reflected, despite the element having no id");
  assert_equals(explicitParent.getAttribute("aria-owns"), "", "Content attribute is empty string, as element has no id");
  assert_equals(axParent.childrenCount, 1, "If explicitly set, even without an ID the node should be in the accessibility tree.");

  child.id = "futureExplicitElement";
  const axChild = accessibilityController.accessibleElementById("futureExplicitElement");

  assert_array_equals(explicitParent.ariaOwnsElements, [child], "Element has the attr associated element now.");
  assert_equals(explicitParent.getAttribute("aria-owns"), "", "Content attribute reflects an empty string because the id was missing at the time of setting.");
  assert_equals(axParent.childrenCount, 1, "After inserting child into DOM and explicitly setting it.")
  assert_equals(axParent.childAtIndex(0), axChild);

  // Remove the explicitly set attr by removing the content attribute.
  explicitParent.removeAttribute("aria-owns");
  assert_equals(explicitParent.ariaOwnsElements, null, "Reflected elements should be null since content attribute was removed.");
  assert_equals(axParent.childrenCount, 0, "Accessible node should have no children since content attribute was removed.");

  // Set the content attribute.
  explicitParent.setAttribute("aria-owns", "futureExplicitElement");
  assert_equals(axParent.childrenCount, 1, "Accessible node should be updated to have child since content attribute was set");
  assert_equals(axParent.childAtIndex(0), axChild);
}, "Explicitly set elements are reflected in the AXTree");
</script>

<div class="container">
  <div id="source1" aria-owns="target1"></div>
  <div id="target"></div>
</div>

<script>
test(function(t) {
  const source1 = document.getElementById("source1");
  const target = document.getElementById("target");
  const axSource1 = accessibilityController.accessibleElementById("source1");

  assert_equals(axSource1.childrenCount, 0);

  target.id = "target1";
  assert_equals(axSource1.childrenCount, 1);

  target.id = "target";
  assert_equals(axSource1.childrenCount, 0);
}, "Aria-owns is dynamically recomputed (2).");
</script>

<div class="container">
    <div id="logical_parent" role="group" aria-owns="logical_1 logical_2 logical_3 logical_4">
        <div id="logical_3">3</div>
        <div id="logical_4">4</div>
        <div id="logical_2">2</div>
        <div id="logical_1">1</div>
    </div>
</div>

<script>
test(function(t)
{
    const axLogicalParent = accessibilityController.accessibleElementById("logical_parent");

    assert_equals(axLogicalParent.childrenCount, 4);
    assert_equals(axLogicalParent.childAtIndex(0).childAtIndex(0).name, "1");
    assert_equals(axLogicalParent.childAtIndex(1).childAtIndex(0).name, "2");
    assert_equals(axLogicalParent.childAtIndex(2).childAtIndex(0).name, "3");
    assert_equals(axLogicalParent.childAtIndex(3).childAtIndex(0).name, "4");

    document.getElementById("logical_parent").setAttribute(
        "aria-owns",
        "logical_2 logical_4 logical_1 logical_3");

    assert_equals(axLogicalParent.childrenCount, 4);
    assert_equals(axLogicalParent.childAtIndex(0).childAtIndex(0).name, "2");
    assert_equals(axLogicalParent.childAtIndex(1).childAtIndex(0).name, "4");
    // assert_equals(axLogicalParent.childAtIndex(2).childAtIndex(0).name, "1");
    // assert_equals(axLogicalParent.childAtIndex(3).childAtIndex(0).name, "3");
}, "A parent can use aria-owns to reorder its children into a more logical AX ordering.");
</script>

<div class="container">
  <div id="original_parent" role="group" aria-owns="kidnapping_target"></div>
  <div id="kidnapping_target">Target</div>
  <div id="kidnapping_parent" role="group"></div>
  <div id="new_parent" role="group"></div>
</div>

<script>
test(function(t) {
    const axOriginalParent = accessibilityController.accessibleElementById("original_parent");
    const axKidnappingParent = accessibilityController.accessibleElementById("kidnapping_parent");
    const axNewParent = accessibilityController.accessibleElementById("new_parent");
    assert_equals(axOriginalParent.childrenCount, 1);
    assert_equals(axOriginalParent.childAtIndex(0).childAtIndex(0).name, "Target");

    // The child cannot be "kidnapped" while a valid aria-owns still exists.
    kidnapping_parent.setAttribute("aria-owns", "kidnapping_target");
    assert_equals(axKidnappingParent.childrenCount, 0);
    assert_equals(axOriginalParent.childrenCount, 1);
    assert_equals(axOriginalParent.childAtIndex(0).childAtIndex(0).name, "Target");
    kidnapping_parent.removeAttribute("aria-owns");

    // If the parentage is removed, the child can be owned by something else.
    original_parent.removeAttribute("aria-owns");

    // TODO(meredithl): investigate how to set this attribute before removing it from
    // the original parent.
    new_parent.setAttribute("aria-owns", "kidnapping_target");
    assert_equals(axNewParent.childrenCount, 1);
    assert_equals(axNewParent.childAtIndex(0).childAtIndex(0).name, "Target");
}, "An element can be reparented by aria-owns if the original relationship is removed");
</script>
p:146