@metamask/snaps-utils
Version:
A collection of utilities for MetaMask Snaps
52 lines • 2.7 kB
JavaScript
/**
* 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