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 );