browser_offscreen_image_in_out_of_process_iframe.js (5765B)
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 DIRPATH = getRootDirectory(gTestPath).replace( 8 "chrome://mochitests/content/", 9 "" 10 ); 11 const parentPATH = DIRPATH + "empty.html"; 12 const iframePATH = DIRPATH + "empty.html"; 13 14 const parentURL = `http://example.com/${parentPATH}`; 15 const iframeURL = `http://example.org/${iframePATH}`; 16 17 add_task(async function setup_pref() { 18 await SpecialPowers.pushPrefEnv({ 19 set: [ 20 // To avoid throttling requestAnimationFrame callbacks in invisible 21 // iframes 22 ["layout.throttled_frame_rate", 60], 23 ], 24 }); 25 }); 26 27 add_task(async function () { 28 const win = await BrowserTestUtils.openNewBrowserWindow({ 29 fission: true, 30 }); 31 32 try { 33 const browser = win.gBrowser.selectedTab.linkedBrowser; 34 35 BrowserTestUtils.startLoadingURIString(browser, parentURL); 36 await BrowserTestUtils.browserLoaded(browser, false, parentURL); 37 38 async function setup(url) { 39 const scroller = content.document.createElement("div"); 40 scroller.style = "width: 300px; height: 300px; overflow: scroll;"; 41 scroller.setAttribute("id", "scroller"); 42 content.document.body.appendChild(scroller); 43 44 // Make a space bigger than display port. 45 const spacer = content.document.createElement("div"); 46 spacer.style = "width: 100%; height: 10000px;"; 47 scroller.appendChild(spacer); 48 49 const iframe = content.document.createElement("iframe"); 50 scroller.appendChild(iframe); 51 52 iframe.contentWindow.location = url; 53 await new Promise(resolve => { 54 iframe.addEventListener("load", resolve, { once: true }); 55 }); 56 57 return iframe.browsingContext; 58 } 59 60 async function setupImage() { 61 const img = content.document.createElement("img"); 62 // This GIF is a 100ms interval animation. 63 img.setAttribute("src", "animated.gif"); 64 img.setAttribute("id", "img"); 65 content.document.body.appendChild(img); 66 67 const spacer = content.document.createElement("div"); 68 spacer.style = "width: 100%; height: 10000px;"; 69 content.document.body.appendChild(spacer); 70 await new Promise(resolve => { 71 img.addEventListener("load", resolve, { once: true }); 72 }); 73 } 74 75 // Returns the count of frameUpdate during |time| (in ms) period. 76 async function observeFrameUpdate(time) { 77 function ImageDecoderObserverStub() { 78 this.sizeAvailable = function sizeAvailable() {}; 79 this.frameComplete = function frameComplete() {}; 80 this.decodeComplete = function decodeComplete() {}; 81 this.loadComplete = function loadComplete() {}; 82 this.frameUpdate = function frameUpdate() {}; 83 this.discard = function discard() {}; 84 this.isAnimated = function isAnimated() {}; 85 this.hasTransparency = function hasTransparency() {}; 86 } 87 88 // Start from the callback of setTimeout. 89 await new Promise(resolve => content.window.setTimeout(resolve, 0)); 90 91 let frameCount = 0; 92 const observer = new ImageDecoderObserverStub(); 93 observer.frameUpdate = () => { 94 frameCount++; 95 }; 96 observer.loadComplete = () => { 97 // Ignore the frameUpdate callback along with loadComplete. It seems 98 // a frameUpdate sometimes happens with a loadComplete when attatching 99 // observer in fission world. 100 frameCount--; 101 }; 102 103 const gObserver = SpecialPowers.Cc["@mozilla.org/image/tools;1"] 104 .getService(SpecialPowers.Ci.imgITools) 105 .createScriptedObserver(observer); 106 const img = content.document.getElementById("img"); 107 108 SpecialPowers.wrap(img).addObserver(gObserver); 109 await new Promise(resolve => content.window.setTimeout(resolve, time)); 110 SpecialPowers.wrap(img).removeObserver(gObserver); 111 112 return frameCount; 113 } 114 115 // Setup an iframe which is initially scrolled out. 116 const iframe = await SpecialPowers.spawn(browser, [iframeURL], setup); 117 118 // Setup a 100ms interval animated GIF image in the iframe. 119 await SpecialPowers.spawn(iframe, [], setupImage); 120 121 let frameCount = await SpecialPowers.spawn( 122 iframe, 123 [1000], 124 observeFrameUpdate 125 ); 126 // Bug 1577084. 127 if (frameCount == 0) { 128 is(frameCount, 0, "no frameupdates"); 129 } else { 130 todo_is(frameCount, 0, "no frameupdates"); 131 } 132 133 // Scroll the iframe into view. 134 await SpecialPowers.spawn(browser, [], async () => { 135 const scroller = content.document.getElementById("scroller"); 136 scroller.scrollTo({ left: 0, top: 9800, behavior: "smooth" }); 137 await new Promise(resolve => content.window.setTimeout(resolve, 1000)); 138 }); 139 140 await new Promise(resolve => requestAnimationFrame(resolve)); 141 142 frameCount = await SpecialPowers.spawn(iframe, [1000], observeFrameUpdate); 143 Assert.greater(frameCount, 0, "There should be frameUpdate(s)"); 144 145 await new Promise(resolve => requestAnimationFrame(resolve)); 146 147 await SpecialPowers.spawn(iframe, [], async () => { 148 const img = content.document.getElementById("img"); 149 // Move the image outside of the scroll port. 'position: absolute' causes 150 // a relow on the image element. 151 img.style = "position: absolute; top: 9000px;"; 152 await new Promise(resolve => 153 content.window.requestAnimationFrame(resolve) 154 ); 155 }); 156 157 await new Promise(resolve => requestAnimationFrame(resolve)); 158 159 frameCount = await SpecialPowers.spawn(iframe, [1000], observeFrameUpdate); 160 is(frameCount, 0, "No frameUpdate should happen"); 161 } finally { 162 await BrowserTestUtils.closeWindow(win); 163 } 164 });