/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

/* import-globals-from ../../mochitest/role.js */
loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });

function* testContainer1(browser, accDoc) {
  const id = 't1_container';
  const docID = getAccessibleDOMNodeID(accDoc);
  const acc = findAccessibleChildByID(accDoc, id);

  /* ================= Initial tree test ==================================== */
  // children are swapped by ARIA owns
  let tree = {
    SECTION: [
      { CHECKBUTTON: [
        { SECTION: [] }
      ] },
      { PUSHBUTTON: [ ] }
    ]
  };
  testAccessibleTree(acc, tree);

  /* ================ Change ARIA owns ====================================== */
  let onReorder = waitForEvent(EVENT_REORDER, id);
  yield invokeSetAttribute(browser, id, 'aria-owns', 't1_button t1_subdiv');
  yield onReorder;

  // children are swapped again, button and subdiv are appended to
  // the children.
  tree = {
    SECTION: [
      { CHECKBUTTON: [ ] }, // checkbox, native order
      { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own
      { SECTION: [ ] } // subdiv from the subtree, ARIA owned
    ]
  };
  testAccessibleTree(acc, tree);

  /* ================ Remove ARIA owns ====================================== */
  onReorder = waitForEvent(EVENT_REORDER, id);
  yield invokeSetAttribute(browser, id, 'aria-owns');
  yield onReorder;

  // children follow the DOM order
  tree = {
    SECTION: [
      { PUSHBUTTON: [ ] },
      { CHECKBUTTON: [
          { SECTION: [] }
      ] }
    ]
  };
  testAccessibleTree(acc, tree);

  /* ================ Set ARIA owns ========================================= */
  onReorder = waitForEvent(EVENT_REORDER, id);
  yield invokeSetAttribute(browser, id, 'aria-owns', 't1_button t1_subdiv');
  yield onReorder;

  // children are swapped again, button and subdiv are appended to
  // the children.
  tree = {
    SECTION: [
      { CHECKBUTTON: [ ] }, // checkbox
      { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own
      { SECTION: [ ] } // subdiv from the subtree, ARIA owned
    ]
  };
  testAccessibleTree(acc, tree);

  /* ================ Add ID to ARIA owns =================================== */
  onReorder = waitForEvent(EVENT_REORDER, docID);
  yield invokeSetAttribute(browser, id, 'aria-owns',
    't1_button t1_subdiv t1_group');
  yield onReorder;

  // children are swapped again, button and subdiv are appended to
  // the children.
  tree = {
    SECTION: [
      { CHECKBUTTON: [ ] }, // t1_checkbox
      { PUSHBUTTON: [ ] }, // button, t1_button
      { SECTION: [ ] }, // subdiv from the subtree, t1_subdiv
      { GROUPING: [ ] } // group from outside, t1_group
    ]
  };
  testAccessibleTree(acc, tree);

  /* ================ Append element ======================================== */
  onReorder = waitForEvent(EVENT_REORDER, id);
  yield ContentTask.spawn(browser, id, contentId => {
    let div = content.document.createElement('div');
    div.setAttribute('id', 't1_child3');
    div.setAttribute('role', 'radio');
    content.document.getElementById(contentId).appendChild(div);
  });
  yield onReorder;

  // children are invalidated, they includes aria-owns swapped kids and
  // newly inserted child.
  tree = {
    SECTION: [
      { CHECKBUTTON: [ ] }, // existing explicit, t1_checkbox
      { RADIOBUTTON: [ ] }, // new explicit, t1_child3
      { PUSHBUTTON: [ ] }, // ARIA owned, t1_button
      { SECTION: [ ] }, // ARIA owned, t1_subdiv
      { GROUPING: [ ] } // ARIA owned, t1_group
    ]
  };
  testAccessibleTree(acc, tree);

  /* ================ Remove element ======================================== */
  onReorder = waitForEvent(EVENT_REORDER, id);
  yield ContentTask.spawn(browser, {}, () =>
    content.document.getElementById('t1_span').remove());
  yield onReorder;

  // subdiv should go away
  tree = {
    SECTION: [
      { CHECKBUTTON: [ ] }, // explicit, t1_checkbox
      { RADIOBUTTON: [ ] }, // explicit, t1_child3
      { PUSHBUTTON: [ ] }, // ARIA owned, t1_button
      { GROUPING: [ ] } // ARIA owned, t1_group
    ]
  };
  testAccessibleTree(acc, tree);

  /* ================ Remove ID ============================================= */
  onReorder = waitForEvent(EVENT_REORDER, docID);
  yield invokeSetAttribute(browser, 't1_group', 'id');
  yield onReorder;

  tree = {
    SECTION: [
      { CHECKBUTTON: [ ] },
      { RADIOBUTTON: [ ] },
      { PUSHBUTTON: [ ] } // ARIA owned, t1_button
    ]
  };
  testAccessibleTree(acc, tree);

  /* ================ Set ID ================================================ */
  onReorder = waitForEvent(EVENT_REORDER, docID);
  yield invokeSetAttribute(browser, 't1_grouptmp', 'id', 't1_group');
  yield onReorder;

  tree = {
    SECTION: [
      { CHECKBUTTON: [ ] },
      { RADIOBUTTON: [ ] },
      { PUSHBUTTON: [ ] }, // ARIA owned, t1_button
      { GROUPING: [ ] } // ARIA owned, t1_group, previously t1_grouptmp
    ]
  };
  testAccessibleTree(acc, tree);
}

function* removeContainer(browser, accDoc) {
  const id = 't2_container1';
  const acc = findAccessibleChildByID(accDoc, id);

  let tree = {
    SECTION: [
      { CHECKBUTTON: [ ] } // ARIA owned, 't2_owned'
    ]
  };
  testAccessibleTree(acc, tree);

  let onReorder = waitForEvent(EVENT_REORDER, id);
  yield ContentTask.spawn(browser, {}, () =>
    content.document.getElementById('t2_container2').removeChild(
      content.document.getElementById('t2_container3')));
  yield onReorder;

  tree = {
    SECTION: [ ]
  };
  testAccessibleTree(acc, tree);
}

function* stealAndRecacheChildren(browser, accDoc) {
  const id1 = 't3_container1';
  const id2 = 't3_container2';
  const acc1 = findAccessibleChildByID(accDoc, id1);
  const acc2 = findAccessibleChildByID(accDoc, id2);

  /* ================ Steal from other ARIA owns ============================ */
  let onReorder = waitForEvent(EVENT_REORDER, id2);
  yield invokeSetAttribute(browser, id2, 'aria-owns', 't3_child');
  yield onReorder;

  let tree = {
    SECTION: [ ]
  };
  testAccessibleTree(acc1, tree);

  tree = {
    SECTION: [
      { CHECKBUTTON: [ ] }
    ]
  };
  testAccessibleTree(acc2, tree);

  /* ================ Append element to recache children ==================== */
  onReorder = waitForEvent(EVENT_REORDER, id2);
  yield ContentTask.spawn(browser, id2, id => {
    let div = content.document.createElement('div');
    div.setAttribute('role', 'radio');
    content.document.getElementById(id).appendChild(div);
  });
  yield onReorder;

  tree = {
    SECTION: [ ]
  };
  testAccessibleTree(acc1, tree);

  tree = {
    SECTION: [
      { RADIOBUTTON: [ ] },
      { CHECKBUTTON: [ ] } // ARIA owned
    ]
  };
  testAccessibleTree(acc2, tree);
}

function* showHiddenElement(browser, accDoc) {
  const id = 't4_container1';
  const acc = findAccessibleChildByID(accDoc, id);

  let tree = {
    SECTION: [
      { RADIOBUTTON: [] }
    ]
  };
  testAccessibleTree(acc, tree);

  let onReorder = waitForEvent(EVENT_REORDER, id);
  yield invokeSetStyle(browser, 't4_child1', 'display', 'block');
  yield onReorder;

  tree = {
    SECTION: [
      { CHECKBUTTON: [] },
      { RADIOBUTTON: [] }
    ]
  };
  testAccessibleTree(acc, tree);
}

function* rearrangeARIAOwns(browser, accDoc) {
  const id = 't5_container';
  const acc = findAccessibleChildByID(accDoc, id);
  const tests = [{
    val: 't5_checkbox t5_radio t5_button',
    roleList: [ 'CHECKBUTTON', 'RADIOBUTTON', 'PUSHBUTTON' ]
  }, {
    val: 't5_radio t5_button t5_checkbox',
    roleList: [ 'RADIOBUTTON', 'PUSHBUTTON', 'CHECKBUTTON' ]
  }];

  for (let { val, roleList } of tests) {
    let onReorder = waitForEvent(EVENT_REORDER, id);
    yield invokeSetAttribute(browser, id, 'aria-owns', val);
    yield onReorder;

    let tree = { SECTION: [ ] };
    for (let role of roleList) {
      let ch = {};
      ch[role] = [];
      tree.SECTION.push(ch);
    }
    testAccessibleTree(acc, tree);
  }
}

function* removeNotARIAOwnedEl(browser, accDoc) {
  const id = 't6_container';
  const acc = findAccessibleChildByID(accDoc, id);

  let tree = {
    SECTION: [
      { TEXT_LEAF: [ ] },
      { GROUPING: [ ] }
    ]
  };
  testAccessibleTree(acc, tree);

  let onReorder = waitForEvent(EVENT_REORDER, id);
  yield ContentTask.spawn(browser, id, contentId => {
    content.document.getElementById(contentId).removeChild(
      content.document.getElementById('t6_span'));
  });
  yield onReorder;

  tree = {
    SECTION: [
      { GROUPING: [ ] }
    ]
  };
  testAccessibleTree(acc, tree);
}

addAccessibleTask('doc_treeupdate_ariaowns.html', function*(browser, accDoc) {
  yield testContainer1(browser, accDoc);
  yield removeContainer(browser, accDoc);
  yield stealAndRecacheChildren(browser, accDoc);
  yield showHiddenElement(browser, accDoc);
  yield rearrangeARIAOwns(browser, accDoc);
  yield removeNotARIAOwnedEl(browser, accDoc);
});
