UNPKG

@metamask/snaps-utils

Version:
52 lines 2.7 kB
/** * Creates the iframe to be used as the execution environment. This may run * forever if the iframe never loads, but the promise should be wrapped in * an initialization timeout in the SnapController. * * @param options - The options for createWindow. * @param options.uri - The iframe URI. * @param options.id - The ID to assign to the iframe. * @param options.sandbox - Whether to enable the sandbox attribute. * @param options.testId - The data-testid attribute to assign to the iframe. * @returns A promise that resolves to the contentWindow of the iframe. */ export async function createWindow({ uri, id, sandbox = true, testId = 'snaps-iframe', }) { return await new Promise((resolve, reject) => { const iframe = document.createElement('iframe'); // The order of operations appears to matter for everything except this // attribute. We may as well set it here. iframe.setAttribute('id', id); iframe.setAttribute('data-testid', testId); if (sandbox) { // For the sandbox property to have any effect it needs to be set before the iframe is appended. // We apply this property as a principle of least authority (POLA) // measure. // Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox iframe.setAttribute('sandbox', 'allow-scripts'); } // In the past, we've had problems that appear to be symptomatic of the // iframe firing the `load` event before its scripts are actually loaded, // which has prevented snaps from executing properly. Therefore, we set // the `src` attribute and append the iframe to the DOM before attaching // the `load` listener. // // `load` should only fire when "all dependent resources" have been // loaded, which includes scripts. // // MDN article for `load` event: https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event // Re: `load` firing twice: https://stackoverflow.com/questions/10781880/dynamically-created-iframe-triggers-onload-event-twice/15880489#15880489 iframe.setAttribute('src', uri); document.body.appendChild(iframe); iframe.addEventListener('load', () => { if (iframe.contentWindow) { resolve(iframe.contentWindow); } else { // We don't know of a case when this would happen, but better to fail // fast if it does. reject(new Error(`iframe.contentWindow not present on load for job "${id}".`)); } }); }); } //# sourceMappingURL=iframe.mjs.map