function imagesPainted() {
  return browser.executeAsync(async (done) => {
    // Await `DOMContentLoaded` to ensure all elements are in the DOM.
    await new Promise((resolve) => {
      if (document.readyState === 'loading') {
        addEventListener('DOMContentLoaded', resolve);
      } else {
        resolve();
      }
    });

    // Use element timing if available, otherwise fall back to load+raf.
    if (PerformanceObserver.supportedEntryTypes.includes('element')) {
      const nodes = new Set([
        ...document.querySelectorAll('[elementtiming]:not([hidden])'),
      ]);

      new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
          if (nodes.has(entry.element)) {
            nodes.delete(entry.element);
          }
        }
        if (nodes.size === 0) {
          // Queue a task so this resolves after other callbacks have run.
          setTimeout(() => done(nodes), 0);
        }
      }).observe({type: 'element', buffered: true});
    } else {
      await new Promise((resolve) => {
        if (document.readyState !== 'complete') {
          addEventListener('load', resolve);
        } else {
          resolve();
        }
      });
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          done();
        });
      });
    }
  });
}
Returns a promise that resolves once the browser window has loaded and all
the images in the document have decoded and rendered.
@return {Promise<void>}