chromium/third_party/blink/web_tests/fast/js/resources/image-preload-helper.js

// This is used by several tests to help get images reliably preloaded.

// Given a node, loads all urls specified in style declarations
// attached to the node or it's descendants.
// imageCount specifies the number of images we expect to find (to try to add some
// protection against brittleness due to imperfect url parsing, since other missing a preload
// will typically result in a test that fails only occasionally).
// If failPattern is specified, then any url that matches the regex
// will be expected to fail to load.
function preloadImagesFromStyle(rootNode, imageCount, onComplete, failPattern) {
    var basePath = location.href.substring(0, location.href.lastIndexOf('/') + 1);
    var nodes = rootNode.querySelectorAll('[style]');
    var imagesToLoad = [];
    var seenUrls = {};
    for (var i = 0; i < nodes.length; i++) {
        var urls = nodes[i].style.cssText.split(/url\w*\("([^)]*)"\)/);
        for (var j = 1; j < urls.length; j += 2) {
            // Attempt to convert URL to a relative path in order to have deterministic error messages.
            var url = urls[j];
            if (url.indexOf(basePath) == 0)
                url = url.substring(basePath.length);

            var error = false;
            if (failPattern && failPattern.test(url))
                error = true;
            if (url in seenUrls)
                continue;
            seenUrls[url] = true;
            imagesToLoad.push({url: url, error: error});
        }
    }

    if (imageCount != imagesToLoad.length) {
        var msg = 'Found the following ' + imagesToLoad.length + ' images, when expecting ' + imageCount + ': ';
        for (var i = 0; i < imagesToLoad.length; i++) {
            msg += '\n' + imagesToLoad[i].url;
        }
        testFailed(msg);
    }

    loadImages(imagesToLoad, onComplete);
}

// For each object in the given array, attempt to load the image specified by the
// url property.  If the error property is specified and true, then the load is
// expected to fail.  Once all loads have completed or failed, onComplete is invoked.
function loadImages(imagesToLoad, onComplete) {

    var imagesLeftToLoad = imagesToLoad.length;

    function onImageLoad(url, success, e) {
        // This debug output order is non-deterministic - only show when not running in DRT
        if (!window.testRunner)
            debug( 'Event "' + e.type + '": ' + url);

        if (!success)
            testFailed('Got unexpected \'' + e.type + '\' event for image: ' + url);
        
        imagesLeftToLoad--;
        if (imagesLeftToLoad == 0) {
            onComplete();
        }
        if (imagesLeftToLoad < 0)
            testFailed('Got more load/error callbacks than expected.');
    }

    for (var i = 0; i < imagesToLoad.length; i++) {
        var img = new Image();
        var expectError = imagesToLoad[i].error;
        img.addEventListener('load', onImageLoad.bind(undefined, imagesToLoad[i].url, !expectError));
        img.addEventListener('error', onImageLoad.bind(undefined, imagesToLoad[i].url, !!expectError));
        img.src = imagesToLoad[i].url;
    }
}