tor-browser

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

browser_test_iframe_transform.js (7119B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const TRANSLATION_OFFSET = 50;
      8 const ELEM_ID = "test-elem-id";
      9 
     10 // Modify the style of an iframe within the content process. This is different
     11 // from, e.g., invokeSetStyle, because this function doesn't rely on
     12 // invokeContentTask, which runs in the context of the iframe itself.
     13 async function invokeSetStyleIframe(browser, id, style, value) {
     14  if (value) {
     15    Logger.log(`Setting ${style} style to ${value} for iframe with id: ${id}`);
     16  } else {
     17    Logger.log(`Removing ${style} style from iframe with id: ${id}`);
     18  }
     19 
     20  // Translate the iframe itself (not content within it).
     21  await SpecialPowers.spawn(
     22    browser,
     23    [id, style, value],
     24    (iframeId, iframeStyle, iframeValue) => {
     25      const elm = content.document.getElementById(iframeId);
     26      if (iframeValue) {
     27        elm.style[iframeStyle] = iframeValue;
     28      } else {
     29        delete elm.style[iframeStyle];
     30      }
     31    }
     32  );
     33 }
     34 
     35 // Test the accessible's bounds, comparing them to the content bounds from DOM.
     36 // This function also accepts an offset, which is necessary in some cases where
     37 // DOM doesn't know about cross-process offsets.
     38 function testBoundsWithOffset(browser, iframeDocAcc, id, domElmBounds, offset) {
     39  // Get the bounds as reported by the accessible.
     40  const acc = findAccessibleChildByID(iframeDocAcc, id);
     41  const accX = {};
     42  const accY = {};
     43  const accWidth = {};
     44  const accHeight = {};
     45  acc.getBounds(accX, accY, accWidth, accHeight);
     46 
     47  // getContentBoundsForDOMElm's result doesn't include iframe translation
     48  // for in-process iframes, but does for out-of-process iframes. To account
     49  // for that here, manually add in the translation offset when examining an
     50  // in-process iframe.
     51  const addTranslationOffset = !gIsRemoteIframe;
     52  const expectedX = addTranslationOffset
     53    ? domElmBounds[0] + offset
     54    : domElmBounds[0];
     55  const expectedY = addTranslationOffset
     56    ? domElmBounds[1] + offset
     57    : domElmBounds[1];
     58  const expectedWidth = domElmBounds[2];
     59  const expectedHeight = domElmBounds[3];
     60 
     61  let boundsAreEquivalent = true;
     62  boundsAreEquivalent &&= accX.value == expectedX;
     63  boundsAreEquivalent &&= accY.value == expectedY;
     64  boundsAreEquivalent &&= accWidth.value == expectedWidth;
     65  boundsAreEquivalent &&= accHeight.value == expectedHeight;
     66  return boundsAreEquivalent;
     67 }
     68 
     69 add_setup(async function () {
     70  await SpecialPowers.pushPrefEnv({
     71    set: [["test.wait300msAfterTabSwitch", true]],
     72  });
     73 });
     74 
     75 addAccessibleTask(
     76  `<div id='${ELEM_ID}'>hello world</div>`,
     77  async function (browser, iframeDocAcc) {
     78    ok(iframeDocAcc, "IFRAME document accessible is present");
     79 
     80    await testBoundsWithContent(iframeDocAcc, ELEM_ID, browser);
     81 
     82    // Translate the iframe, which should modify cross-process offset.
     83    await invokeSetStyleIframe(
     84      browser,
     85      DEFAULT_IFRAME_ID,
     86      "transform",
     87      `translate(${TRANSLATION_OFFSET}px, ${TRANSLATION_OFFSET}px)`
     88    );
     89 
     90    // Allow content to advance to update DOM, then capture the DOM bounds.
     91    await waitForContentPaint(browser);
     92    const domElmBoundsAfterTranslate = await getContentBoundsForDOMElm(
     93      browser,
     94      ELEM_ID
     95    );
     96 
     97    // Ensure that there's enough time for the cache to update.
     98    await untilCacheOk(() => {
     99      return testBoundsWithOffset(
    100        browser,
    101        iframeDocAcc,
    102        ELEM_ID,
    103        domElmBoundsAfterTranslate,
    104        TRANSLATION_OFFSET
    105      );
    106    }, "Accessible bounds have changed in the cache and match DOM bounds.");
    107 
    108    // Adjust padding of the iframe, then verify bounds adjust properly.
    109    // iframes already have a border by default, so we check padding here.
    110    const PADDING_OFFSET = 100;
    111    await invokeSetStyleIframe(
    112      browser,
    113      DEFAULT_IFRAME_ID,
    114      "padding",
    115      `${PADDING_OFFSET}px`
    116    );
    117 
    118    // Allow content to advance to update DOM, then capture the DOM bounds.
    119    await waitForContentPaint(browser);
    120    const domElmBoundsAfterAddingPadding = await getContentBoundsForDOMElm(
    121      browser,
    122      ELEM_ID
    123    );
    124 
    125    await untilCacheOk(() => {
    126      return testBoundsWithOffset(
    127        browser,
    128        iframeDocAcc,
    129        ELEM_ID,
    130        domElmBoundsAfterAddingPadding,
    131        TRANSLATION_OFFSET
    132      );
    133    }, "Accessible bounds have changed in the cache and match DOM bounds.");
    134  },
    135  {
    136    topLevel: false,
    137    iframe: true,
    138    remoteIframe: true,
    139    iframeAttrs: {
    140      style: `height: 100px; width: 100px;`,
    141    },
    142  }
    143 );
    144 
    145 /**
    146 * Test document bounds change notifications.
    147 * Note: This uses iframes to change the doc container size in order
    148 * to have the doc accessible's bounds change.
    149 */
    150 addAccessibleTask(
    151  `<div id="div" style="width: 30px; height: 30px"></div>`,
    152  async function (browser, accDoc) {
    153    const docWidth = () => {
    154      let width = {};
    155      accDoc.getBounds({}, {}, width, {});
    156      return width.value;
    157    };
    158 
    159    await untilCacheIs(docWidth, 0, "Doc width is 0");
    160    await invokeSetStyleIframe(browser, DEFAULT_IFRAME_ID, "width", `300px`);
    161    await untilCacheIs(docWidth, 300, "Doc width is 300");
    162  },
    163  {
    164    chrome: false,
    165    topLevel: false,
    166    iframe: true,
    167    remoteIframe: true,
    168    iframeAttrs: { style: "width: 0;" },
    169  }
    170 );
    171 
    172 /**
    173 * Test document bounds after re-creating an iframe.
    174 */
    175 addAccessibleTask(
    176  `
    177 <ol id="ol">
    178  <iframe id="iframe" src="data:text/html,"></iframe>
    179 </ol>
    180  `,
    181  async function (browser, docAcc) {
    182    let iframeDoc = findAccessibleChildByID(docAcc, "iframe").firstChild;
    183    ok(iframeDoc, "Got the iframe document");
    184    const origX = {};
    185    const origY = {};
    186    iframeDoc.getBounds(origX, origY, {}, {});
    187    let reordered = waitForEvent(EVENT_REORDER, docAcc);
    188    await invokeContentTask(browser, [], () => {
    189      // This will cause a bounds cache update to be queued for the iframe doc.
    190      content.document.getElementById("iframe").width = "600";
    191      // This will recreate the ol a11y subtree, including the iframe. The
    192      // iframe document will be unbound briefly while this happens. We want to
    193      // be sure processing the bounds cache update queued above doesn't assert
    194      // while the document is unbound. The setTimeout is necessary to get the
    195      // cache update to happen at the right time.
    196      content.setTimeout(
    197        () => (content.document.getElementById("ol").type = "i"),
    198        0
    199      );
    200    });
    201    await reordered;
    202    const iframe = findAccessibleChildByID(docAcc, "iframe");
    203    // We don't currently fire an event when a DocAccessible is re-bound to a new OuterDoc.
    204    await BrowserTestUtils.waitForCondition(() => iframe.firstChild);
    205    iframeDoc = iframe.firstChild;
    206    ok(iframeDoc, "Got the iframe document after re-creation");
    207    const newX = {};
    208    const newY = {};
    209    iframeDoc.getBounds(newX, newY, {}, {});
    210    ok(
    211      origX.value == newX.value && origY.value == newY.value,
    212      "Iframe document x and y are same after iframe re-creation"
    213    );
    214  }
    215 );