tor-browser

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

exec-command-without-editable-element.tentative.html (25459B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>Test that execCommand without editable element</title>
      4 <script src=../include/implementation.js></script>
      5 <script>var testsJsLibraryOnly = true</script>
      6 <script src=../include/tests.js></script>
      7 <script src=/resources/testharness.js></script>
      8 <script src=/resources/testharnessreport.js></script>
      9 <script>
     10 "use strict";
     11 
     12 setup({explicit_done: true});
     13 
     14 // This test calls execCommand() without editable element in the document,
     15 // but its parent or child document has editable element and it has focus.
     16 // In most cases, execCommand() should do nothing and return false.  However,
     17 // "cut", "copy", "paste" and "selectall" commands should work without DOM tree
     18 // modification for making web apps can implement their own editor without
     19 // editable element.
     20 async function runTests() {
     21  let parentWindow = window;
     22  let parentDocument = document;
     23  let parentSelection = parentDocument.getSelection();
     24  let parentEditor = parentDocument.getElementById("editor");
     25  parentEditor.focus();
     26  let iframe = document.getElementsByTagName("iframe")[0];
     27  let childWindow = iframe.contentWindow;
     28  let childDocument = iframe.contentDocument;
     29  let childSelection = childDocument.getSelection();
     30  let childEditor = childDocument.getElementById("editor");
     31  childEditor.focus();
     32 
     33  // execCommand() in child document shouldn't affect to focused parent
     34  // document.
     35  await doTest(parentWindow, parentDocument, parentSelection, parentEditor,
     36               childWindow, childDocument, childSelection, childEditor, false);
     37  // execCommand() in parent document shouldn't affect to focused child
     38  // document but "cut" and "copy" may affect the focused child document.
     39  await doTest(childWindow, childDocument, childSelection, childEditor,
     40               parentWindow, parentDocument, parentSelection, parentEditor, true);
     41 
     42  done();
     43 }
     44 
     45 async function doTest(aFocusWindow, aFocusDocument, aFocusSelection, aFocusEditor,
     46                      aExecWindow, aExecDocument, aExecSelection, aExecEditor,
     47                      aExecInParent) {
     48  const kTests = [
     49    /**
     50     * command: The command which you test.
     51     * focusContent: Will be set to innerHTML of div#editor element in focused
     52     *               document.
     53     * execContent: Will be set to innerHTML of div#editor element in the
     54     *              document whose execCommand() will be called.
     55     * initFunc: [optional] If you need to do something before running the
     56     *           test, you can do it with a function.
     57     * expectedFocusContent: Expected content and selection in div#editor in
     58     *                       focused document after calling execCommand().
     59     * expectedExecContent: Expected content and selection in div#editor in
     60     *                      the document whose execCommand() is called.
     61     * event: The event which you need to check whether it's fired or not.
     62     * expectedFiredInFocus: true if the event should be fired on the focused
     63     *                       document node.
     64     * expectedFiredInExec: true if the event should be fired on the document
     65     *                      node whose execCommand() is called.
     66     * expectedResult: Expected result of execCommand().
     67     */
     68    {command: "bold", value: "bold",
     69     focusContent: "a[b]c", execContent: "a[b]c",
     70     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
     71     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
     72     expectedResult: false,
     73    },
     74    {command: "italic", value: null,
     75     focusContent: "a[b]c", execContent: "a[b]c",
     76     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
     77     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
     78     expectedResult: false,
     79    },
     80    {command: "underline", value: null,
     81     focusContent: "a[b]c", execContent: "a[b]c",
     82     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
     83     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
     84     expectedResult: false,
     85    },
     86    {command: "strikethrough", value: null,
     87     focusContent: "a[b]c", execContent: "a[b]c",
     88     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
     89     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
     90     expectedResult: false,
     91    },
     92    {command: "subscript", value: null,
     93     focusContent: "a[b]c", execContent: "a[b]c",
     94     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
     95     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
     96     expectedResult: false,
     97    },
     98    {command: "superscript", value: null,
     99     focusContent: "a[b]c", execContent: "a[b]c",
    100     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    101     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    102     expectedResult: false,
    103    },
    104    // "cut", "copy" and "paste" command should cause firing corresponding
    105    // events to make web apps be able to implement their own editor even
    106    // if there is no editor and selection is collapsed.
    107    {command: "cut", value: null,
    108     focusContent: "a[b]c", execContent: "ab[]c",
    109     expectedFocusContent: "a[b]c", expectedExecContent: "ab[]c",
    110     event: "cut", expectedFiredInFocus: false, expectedFiredInExec: true,
    111     expectedResult: false,
    112    },
    113    {command: "cut", value: null,
    114     focusContent: "a[b]c", execContent: "a[b]c",
    115     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    116     event: "cut", expectedFiredInFocus: false, expectedFiredInExec: true,
    117     expectedResult: false,
    118    },
    119    {command: "copy", value: null,
    120     focusContent: "a[b]c", execContent: "ab[]c",
    121     expectedFocusContent: "a[b]c", expectedExecContent: "ab[]c",
    122     event: "copy", expectedFiredInFocus: false, expectedFiredInExec: true,
    123     expectedResult: false,
    124    },
    125    {command: "copy", value: null,
    126     focusContent: "a[b]c", execContent: "a[b]c",
    127     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    128     event: "copy", expectedFiredInFocus: false, expectedFiredInExec: true,
    129     expectedResult: false,
    130    },
    131    {command: "paste", value: null,
    132     focusContent: "a[b]c", execContent: "a[b]c",
    133     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    134     initFunc: () => { aFocusDocument.execCommand("copy", false, "b"); },
    135     event: "paste", expectedFiredInFocus: false, expectedFiredInExec: true,
    136     expectedResult: false,
    137    },
    138    {command: "delete", value: null,
    139     focusContent: "a[b]c", execContent: "a[b]c",
    140     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    141     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    142     expectedResult: false,
    143    },
    144    {command: "forwarddelete", value: null,
    145     focusContent: "a[b]c", execContent: "a[b]c",
    146     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    147     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    148     expectedResult: false,
    149    },
    150    // "selectall" command should be available without editable content.
    151    {command: "selectall", value: null,
    152     focusContent: "a[b]c", execContent: "a[b]c",
    153     expectedFocusContent: "a[b]c", expectedExecContent: undefined,
    154     event: "selectionchange", expectedFiredInFocus: false, expectedFiredInExec: true,
    155     expectedResult: true,
    156    },
    157    {command: "undo", value: null,
    158     focusContent: "a[]c", execContent: "a[b]c",
    159     initFunc: () => { aFocusDocument.execCommand("insertText", false, "b"); },
    160     expectedFocusContent: "ab[]c", expectedExecContent: "a[b]c",
    161     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    162     expectedResult: false,
    163    },
    164    {command: "redo", value: null,
    165     focusContent: "a[]c", execContent: "a[b]c",
    166     initFunc: () => {
    167       aFocusDocument.execCommand("insertText", false, "b");
    168       aFocusDocument.execCommand("undo", false, null);
    169     },
    170     expectedFocusContent: "a[]c", expectedExecContent: "a[b]c",
    171     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    172     expectedResult: false,
    173    },
    174    {command: "indent", value: null,
    175     focusContent: "a[b]c", execContent: "a[b]c",
    176     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    177     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    178     expectedResult: false,
    179    },
    180    {command: "outdent", value: null,
    181     focusContent: "a[b]c", execContent: "a[b]c",
    182     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    183     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    184     expectedResult: false,
    185    },
    186    {command: "backcolor", value: "#000000",
    187     focusContent: "a[b]c", execContent: "a[b]c",
    188     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    189     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    190     expectedResult: false,
    191    },
    192    {command: "forecolor", value: "#F0F0F0",
    193     focusContent: "a[b]c", execContent: "a[b]c",
    194     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    195     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    196     expectedResult: false,
    197    },
    198    {command: "hilitecolor", value: "#FFFF00",
    199     focusContent: "a[b]c", execContent: "a[b]c",
    200     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    201     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    202     expectedResult: false,
    203    },
    204    {command: "fontname", value: "DummyFont",
    205     focusContent: "a[b]c", execContent: "a[b]c",
    206     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    207     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    208     expectedResult: false,
    209    },
    210    {command: "fontsize", value: "5",
    211     focusContent: "a[b]c", execContent: "a[b]c",
    212     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    213     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    214     expectedResult: false,
    215    },
    216    {command: "increasefontsize", value: null,
    217     focusContent: "a[b]c", execContent: "a[b]c",
    218     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    219     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    220     expectedResult: false,
    221    },
    222    {command: "decreasefontsize", value: null,
    223     focusContent: "a[b]c", execContent: "a[b]c",
    224     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    225     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    226     expectedResult: false,
    227    },
    228    {command: "inserthorizontalrule", value: null,
    229     focusContent: "a[]bc", execContent: "a[]bc",
    230     expectedFocusContent: "a[]bc", expectedExecContent: "a[]bc",
    231     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    232     expectedResult: false,
    233    },
    234    {command: "createlink", value: "foo.html",
    235     focusContent: "a[b]c", execContent: "a[b]c",
    236     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    237     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    238     expectedResult: false,
    239    },
    240    {command: "insertimage", value: "no-image.png",
    241     focusContent: "a[b]c", execContent: "a[b]c",
    242     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    243     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    244     expectedResult: false,
    245    },
    246    {command: "inserthtml", value: "<b>inserted</b>",
    247     focusContent: "a[b]c", execContent: "a[b]c",
    248     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    249     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    250     expectedResult: false,
    251    },
    252    {command: "inserttext", value: "**inserted**",
    253     focusContent: "a[b]c", execContent: "a[b]c",
    254     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    255     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    256     expectedResult: false,
    257    },
    258    {command: "justifyleft", value: null,
    259     focusContent: "a[b]c", execContent: "a[b]c",
    260     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    261     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    262     expectedResult: false,
    263    },
    264    {command: "justifyright", value: null,
    265     focusContent: "a[b]c", execContent: "a[b]c",
    266     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    267     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    268     expectedResult: false,
    269    },
    270    {command: "justifycenter", value: null,
    271     focusContent: "a[b]c", execContent: "a[b]c",
    272     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    273     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    274     expectedResult: false,
    275    },
    276    {command: "justifyfull", value: null,
    277     focusContent: "a[b]c", execContent: "a[b]c",
    278     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    279     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    280     expectedResult: false,
    281    },
    282    {command: "removeformat", value: null,
    283     focusContent: "<b>a[b]c</b>", execContent: "<b>a[b]c</b>",
    284     expectedFocusContent: "<b>a[b]c</b>", expectedExecContent: "<b>a[b]c</b>",
    285     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    286     expectedResult: false,
    287    },
    288    {command: "unlink", value: null,
    289     focusContent: "<a href=\"foo.html\">a[b]c</a>", execContent: "<a href=\"foo.html\">a[b]c</a>",
    290     expectedFocusContent: "<a href=\"foo.html\">a[b]c</a>", expectedExecContent: "<a href=\"foo.html\">a[b]c</a>",
    291     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    292     expectedResult: false,
    293    },
    294    {command: "insertorderedlist", value: null,
    295     focusContent: "a[b]c", execContent: "a[b]c",
    296     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    297     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    298     expectedResult: false,
    299    },
    300    {command: "insertunorderedlist", value: null,
    301     focusContent: "a[b]c", execContent: "a[b]c",
    302     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    303     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    304     expectedResult: false,
    305    },
    306    {command: "insertparagraph", value: null,
    307     focusContent: "a[b]c", execContent: "a[b]c",
    308     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    309     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    310     expectedResult: false,
    311    },
    312    {command: "insertlinebreak", value: null,
    313     focusContent: "a[b]c", execContent: "a[b]c",
    314     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    315     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    316     expectedResult: false,
    317    },
    318    {command: "formatblock", value: "div",
    319     focusContent: "a[b]c", execContent: "a[b]c",
    320     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    321     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    322     expectedResult: false,
    323    },
    324    {command: "heading", value: "h1",
    325     focusContent: "a[b]c", execContent: "a[b]c",
    326     expectedFocusContent: "a[b]c", expectedExecContent: "a[b]c",
    327     event: "input", expectedFiredInFocus: false, expectedFiredInExec: false,
    328     expectedResult: false,
    329    },
    330    /**
    331     * command: The command which you test.
    332     * state: The state which is used with execCommand().
    333     * initState: The state which should be set with execCommand() first.
    334     * focusContent: Will be set to innerHTML of div#editor element in focused
    335     *               document.
    336     * execContent: Will be set to innerHTML of div#editor element in the
    337     *              document whose execCommand() will be called.
    338     * initFunc: [optional] If you need to do something before running the
    339     *           test, you can do it with a function.
    340     * expectedSetStateInFocus: Expected queryCommandState() result in focused
    341     *                          document.
    342     * expectedSetStateInExec: Expected queryCommandState() result in document
    343     *                         whose execCommand() is called.
    344     * expectedResult: Expected result of execCommand().
    345     */
    346    {command: "styleWithCSS", state: "true", initState: "false",
    347     focusContent: "a[b]c", execContent: "a[b]c",
    348     expectedSetStateInFocus: false, expectedSetStateInExec: false,
    349     expectedResult: false,
    350    },
    351    {command: "contentReadOnly", state: "true", initState: "false",
    352     focusContent: "a[b]c", execContent: "a[b]c",
    353     expectedSetStateInFocus: false, expectedSetStateInExec: false,
    354     expectedResult: false,
    355    },
    356    {command: "insertBrOnReturn", state: "true", initState: "false",
    357     focusContent: "a[b]c", execContent: "a[b]c",
    358     expectedSetStateInFocus: false, expectedSetStateInExec: false,
    359     expectedResult: false,
    360    },
    361    {command: "defaultParagraphSeparator", state: "div", initState: "p",
    362     focusContent: "a[b]c", execContent: "a[b]c",
    363     expectedSetStateInFocus: false, expectedSetStateInExec: false,
    364     expectedResult: false,
    365    },
    366    {command: "defaultParagraphSeparator", state: "p", initState: "div",
    367     focusContent: "a[b]c", execContent: "a[b]c",
    368     expectedSetStateInFocus: false, expectedSetStateInExec: false,
    369     expectedResult: false,
    370    },
    371    {command: "enableObjectResizing", state: "true", initState: "false",
    372     focusContent: "a[b]c", execContent: "a[b]c",
    373     expectedSetStateInFocus: false, expectedSetStateInExec: false,
    374     expectedResult: false,
    375    },
    376    {command: "enableInlineTableEditing", state: "true", initState: "false",
    377     focusContent: "a[b]c", execContent: "a[b]c",
    378     expectedSetStateInFocus: false, expectedSetStateInExec: false,
    379     expectedResult: false,
    380    },
    381    {command: "enableAbsolutePositionEditing", state: "true", initState: "false",
    382     focusContent: "a[b]c", execContent: "a[b]c",
    383     expectedSetStateInFocus: false, expectedSetStateInExec: false,
    384     expectedResult: false,
    385    },
    386  ];
    387 
    388  async function waitForCondition(aCheckFunc) {
    389    let retry = 60;
    390    while (retry--) {
    391      if (aCheckFunc()) {
    392        return;
    393      }
    394      await new Promise(resolve => requestAnimationFrame(resolve));
    395    }
    396  }
    397 
    398  for (const kTest of kTests) {
    399    // Skip unsupported command since it's not purpose of this tests whether
    400    // each command is supported on the browser.
    401    if (!aExecDocument.queryCommandSupported(kTest.command)) {
    402      continue;
    403    }
    404    aExecEditor.removeAttribute("contenteditable");  // Disable commands in the exec document.
    405    let points = setupDiv(aFocusEditor, kTest.focusContent);
    406    aFocusSelection.setBaseAndExtent(points[0], points[1], points[2], points[3]);
    407    points = setupDiv(aExecEditor, kTest.execContent);
    408    aExecSelection.setBaseAndExtent(points[0], points[1], points[2], points[3]);
    409    aFocusWindow.focus();
    410    aFocusEditor.focus();
    411    if (kTest.initFunc) {
    412      kTest.initFunc();
    413    }
    414    if (kTest.state === undefined) {
    415      let eventFiredOnFocusDocument = false;
    416      function handlerOnFocusDocument() {
    417        eventFiredOnFocusDocument = true;
    418      }
    419      aFocusDocument.addEventListener(kTest.event, handlerOnFocusDocument, {capture: true});
    420      let eventFiredOnExecDocument = false;
    421      function handlerOnExecDocument() {
    422        eventFiredOnExecDocument = true;
    423      }
    424      aExecDocument.addEventListener(kTest.event, handlerOnExecDocument, {capture: true});
    425      const kDescription = `${aExecInParent ? "Parent" : "Child"}Document.execCommand(${kTest.command}, false, ${kTest.value}) with ${kTest.execContent}`;
    426      test(function () {
    427        let ret = aExecDocument.execCommand(kTest.command, false, kTest.value);
    428        assert_equals(ret, kTest.expectedResult, `execCommand should return ${kTest.expectedResult}`);
    429      }, `${kDescription}: calling execCommand`);
    430      if (kTest.event === "selectionchange") {
    431        test(function () {
    432          assert_false(eventFiredOnFocusDocument,
    433                      `"${kTest.event}" event should not be fired synchronously on focused document`);
    434          assert_false(eventFiredOnExecDocument,
    435                      `"${kTest.event}" event should not be fired synchronously on executed document`);
    436        }, `${kDescription}: checking unexpected synchronous event`);
    437        await waitForCondition(() => eventFiredOnFocusDocument && eventFiredOnExecDocument);
    438        // TODO: Whether select all changes selection in the focused document depends on the
    439        //       implementation of "Select All".
    440      } else {
    441        test(function () {
    442          assert_equals(eventFiredOnFocusDocument, kTest.expectedFiredInFocus,
    443                      `"${kTest.event}" event should${kTest.expectedFiredInFocus ? "" : " not"} be fired`);
    444        }, `${kDescription}: checking event on focused document`);
    445      }
    446      test(function () {
    447        assert_equals(eventFiredOnExecDocument, kTest.expectedFiredInExec,
    448                    `"${kTest.event}" event should${kTest.expectedFiredInExec ? "" : " not"} be fired`);
    449      }, `${kDescription}: checking event on executed document`);
    450      test(function () {
    451        if (aFocusSelection.rangeCount) {
    452          addBrackets(aFocusSelection.getRangeAt(0));
    453        }
    454        assert_equals(aFocusEditor.innerHTML, kTest.expectedFocusContent);
    455      }, `${kDescription}: checking result content in focused document`);
    456      test(function () {
    457        if (kTest.command === "selectall") {
    458          assert_true(aExecSelection.rangeCount > 0);
    459          assert_equals(
    460            aExecSelection.toString().replace(/[\r\n]/g, ""),
    461            aExecDocument.body.textContent.replace(/[\r\n]/g, "")
    462          );
    463        } else {
    464          if (aExecSelection.rangeCount) {
    465            addBrackets(aExecSelection.getRangeAt(0));
    466          }
    467          assert_equals(aExecEditor.innerHTML, kTest.expectedExecContent);
    468        }
    469      }, `${kDescription}: checking result content in executed document`);
    470      aFocusDocument.removeEventListener(kTest.event, handlerOnFocusDocument, {capture: true});
    471      aExecDocument.removeEventListener(kTest.event, handlerOnExecDocument, {capture: true});
    472      aExecEditor.setAttribute("contenteditable", "");
    473    } else {
    474      const kDescription = `${aExecInParent ? "Parent" : "Child"}Document.execCommand(${kTest.command}, false, ${kTest.state})`;
    475      test(function () {
    476        let ret = aExecDocument.execCommand(kTest.command, false, kTest.initState);
    477        assert_equals(ret, kTest.expectedResult, `execCommand should return ${kTest.expectedResult}`);
    478      }, `${kDescription}: calling execCommand to initialize`);
    479      let hasSetState = false;
    480      test(function () {
    481        hasSetState = aExecDocument.queryCommandState(kTest.command);
    482        assert_equals(hasSetState, kTest.expectedSetStateInExec, `queryCommandState on executed document should return ${kTest.expectedSetState}`);
    483      }, `${kDescription}: calling queryCommandState on executed document after initializing`);
    484      test(function () {
    485        let ret = aFocusDocument.queryCommandState(kTest.command);
    486        assert_equals(ret, kTest.expectedSetStateInFocus, `queryCommandState on focus document should return ${kTest.expectedSetState}`);
    487      }, `${kDescription}: calling queryCommandState on focus document after initializing`);
    488      if (hasSetState) {
    489        test(function () {
    490          let ret = aExecDocument.queryCommandValue(kTest.command);
    491          assert_equals(ret, kTest.initState, `queryCommandValue on executed document should return ${kTest.initState}`);
    492        }, `${kDescription}: calling queryCommandValue on executed document after initializing`);
    493      }
    494      test(function () {
    495        let ret = aExecDocument.execCommand(kTest.command, false, kTest.state);
    496        assert_equals(ret, kTest.expectedResult, `execCommand should return ${kTest.expectedResult}`);
    497      }, `${kDescription}: calling execCommand to set state`);
    498      test(function () {
    499        hasSetState = aExecDocument.queryCommandState(kTest.command);
    500        assert_equals(hasSetState, kTest.expectedSetStateInExec, `queryCommandState should return ${kTest.expectedSetState}`);
    501      }, `${kDescription}: calling queryCommandState on executed document`);
    502      test(function () {
    503        let ret = aFocusDocument.queryCommandState(kTest.command);
    504        assert_equals(ret, kTest.expectedSetStateInFocus, `queryCommandState should return ${kTest.expectedSetState}`);
    505      }, `${kDescription}: calling queryCommandState on focused document`);
    506      if (hasSetState) {
    507        test(function () {
    508          let ret = aExecDocument.queryCommandValue(kTest.command);
    509          assert_equals(ret, kTest.state, `queryCommandValue should return ${kTest.initState}`);
    510        }, `${kDescription}: calling queryCommandValue on executed document`);
    511      }
    512      aExecEditor.setAttribute("contenteditable", "");
    513      test(function () {
    514        let ret = aExecDocument.queryCommandState(kTest.command);
    515        assert_equals(ret, kTest.expectedSetStateInExec, `queryCommandState should return ${kTest.expectedSetState}`);
    516      }, `${kDescription}: calling queryCommandState on executed document after making executed document editable`);
    517    }
    518  }
    519 }
    520 
    521 window.addEventListener("load", runTests, {once: true});
    522 </script>
    523 <body>
    524 <div contenteditable id="editor">abc</div>
    525 <iframe srcdoc="<div contenteditable id='editor'>def</div><span>ghi</span>"></iframe>
    526 </body>