tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

dialog-popover-closedby-complex.html (6585B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <meta name="timeout" content="long">
      4 <link rel="author" href="mailto:masonf@chromium.org">
      5 <link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-light-dismiss">
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 <script src="/resources/testdriver.js"></script>
      9 <script src="/resources/testdriver-actions.js"></script>
     10 <script src="/resources/testdriver-vendor.js"></script>
     11 <script src="../../popovers/resources/popover-utils.js"></script>
     12 
     13 <div id=unrelated>Unrelated</div>
     14 
     15 <div class=testcase data-description="normal DOM nesting">
     16  <dialog class=dialogA closedby=any style="position: fixed; top: 100px; bottom: auto; padding:0;">Dialog 1
     17    <div class=popoverA popover style="top: 150px; bottom: auto; padding:0;">Popover 1
     18      <dialog class=dialogB closedby=any style="position: fixed; top: 200px; bottom: auto; padding:0;">Dialog 2
     19        <div class=popoverB popover style="top: 250px; bottom: auto; padding:0;">Popover 2</div>
     20      </dialog>
     21    </div>
     22  </dialog>
     23 </div>
     24 
     25 <div class=testcase data-description="same structure, but with shadow DOM slots">
     26  <div>
     27    <template shadowrootmode=open>
     28      <dialog class=dialogA closedby=any style="position: fixed; top: 100px; bottom: auto; padding:0;">Dialog 1
     29        <slot></slot>
     30      </dialog>
     31    </template>
     32    <div>
     33      <template shadowrootmode=open>
     34        <div class=popoverA popover style="top: 150px; bottom: auto; padding:0;">Popover 1
     35          <slot></slot>
     36        </div>
     37      </template>
     38      <div>
     39        <template shadowrootmode=open>
     40          <dialog class=dialogB closedby=any style="position: fixed; top: 200px; bottom: auto; padding:0;">Dialog 2
     41            <slot></slot>
     42          </dialog>
     43        </template>
     44        <div class=popoverB popover style="top: 250px; bottom: auto; padding:0;">Popover 2</div>
     45      </div>
     46    </div>
     47  </div>
     48 </div>
     49 
     50 <script>
     51 function openDialog(dialog,modal) {
     52  assert_true(!!dialog);
     53  assert_false(dialog.open);
     54  if (modal) {
     55    dialog.showModal();
     56  } else {
     57    dialog.show();
     58  }
     59  assert_true(dialog.open);
     60  assert_equals(dialog.matches(':modal'),modal);
     61 }
     62 function assertStates(elements,dialogAExpected,popoverAExpected,
     63    dialogBExpected,popoverBExpected) {
     64  assert_equals(elements.dialogA.open,dialogAExpected,
     65    `dialogA should be ${dialogAExpected ? 'open' : 'closed'}`);
     66  assert_equals(elements.popoverA.matches(':popover-open'),popoverAExpected,
     67    `popoverA should be ${popoverAExpected ? 'open' : 'closed'}`);
     68  assert_equals(elements.dialogB.open,dialogBExpected,
     69    `dialogB should be ${dialogBExpected ? 'open' : 'closed'}`);
     70  assert_equals(elements.popoverB.matches(':popover-open'),popoverBExpected,
     71    `popoverB should be ${popoverBExpected ? 'open' : 'closed'}`);
     72 }
     73 function findElements(wrapper) {
     74  let elements = {};
     75  if (!wrapper) {
     76    return elements;
     77  }
     78  Array.from(wrapper.children).forEach(child => {
     79    ['dialogA','popoverA','dialogB','popoverB'].forEach(pattern => {
     80      if (child.matches(`.${pattern}`)) {
     81        assert_false(elements.hasOwnProperty(pattern),'Multiple elements with the same class');
     82        elements[pattern] = child;
     83      }
     84      elements = {...elements, ...findElements(child)};
     85      elements = {...elements, ...findElements(child.shadowRoot)};
     86    });
     87  });
     88  return elements;
     89 }
     90 function openDialogPopoverStack(t,elements,modalA,modalB) {
     91  t.add_cleanup(() => {
     92    elements.dialogA.close();
     93    elements.popoverA.hidePopover();
     94    elements.dialogB.close();
     95    elements.popoverB.hidePopover();
     96  });
     97  openDialog(elements.dialogA,modalA);
     98  elements.popoverA.showPopover();
     99  openDialog(elements.dialogB,modalB);
    100  elements.popoverB.showPopover();
    101  assertStates(elements,true,true,true,true);
    102 }
    103 
    104 document.querySelectorAll('.testcase').forEach(testcase => {
    105  const testDescription = `, ${testcase.dataset.description}`;
    106  const elements = findElements(testcase);
    107  assert_array_equals(Object.keys(elements).sort(),['dialogA','popoverA','dialogB','popoverB'].sort());
    108  [false,true].forEach(modalA => {
    109    [false,true].forEach(modalB => {
    110      const modalAString = modalA ? 'modal dialogA' : 'modeless dialogA';
    111      const modalBString = modalB ? 'modal dialogB' : 'modeless dialogB';
    112      promise_test(async (t) => {
    113        openDialogPopoverStack(t,elements,modalA,modalB);
    114        await clickOn(unrelated);
    115        // Clicking outside all is actually a click on a dialog backdrop.
    116        // If dialogB is modal, it'll be dialogB, which is nested inside popoverA.
    117        // Either way, both popoverB and dialogB should close.
    118        assertStates(elements,true,modalB,false,false);
    119        await clickOn(unrelated);
    120        // Clicking outside again should close the remaining two.
    121        assertStates(elements,false,false,false,false);
    122      },`clicking outside all with ${modalAString} and ${modalBString}${testDescription}`);
    123 
    124      promise_test(async (t) => {
    125        openDialogPopoverStack(t,elements,modalA,modalB);
    126        await clickOn(elements.popoverB);
    127        // Clicking popoverB should keep everything open.
    128        assertStates(elements,true,true,true,true);
    129      },`clicking popoverB with ${modalAString} and ${modalBString}${testDescription}`);
    130 
    131      promise_test(async (t) => {
    132        openDialogPopoverStack(t,elements,modalA,modalB);
    133        await clickOn(elements.dialogB);
    134        // Only popoverB should be light dismissed.
    135        assertStates(elements,true,true,true,false);
    136      },`clicking dialogB with ${modalAString} and ${modalBString}${testDescription}`);
    137 
    138      promise_test(async (t) => {
    139        openDialogPopoverStack(t,elements,modalA,modalB);
    140        await clickOn(elements.popoverA);
    141        // Both dialogB and popoverB should be light dismissed.
    142        assertStates(elements,true,true,false,false);
    143      },`clicking popoverA with ${modalAString} and ${modalBString}${testDescription}`);
    144 
    145      promise_test(async (t) => {
    146        openDialogPopoverStack(t,elements,modalA,modalB);
    147        await clickOn(elements.dialogA);
    148        // If dialogB is modal, clicking on dialogA is actually clicking on dialogB,
    149        // which means popoverB will stay open.
    150        assertStates(elements,true,modalB,false,false);
    151        await clickOn(elements.dialogA);
    152        // The next click on dialogA should light dismiss popoverA.
    153        assertStates(elements,true,false,false,false);
    154      },`clicking dialogA with ${modalAString} and ${modalBString}${testDescription}`);
    155    });
    156  });
    157 });
    158 </script>