Resource Timing - What's included

We have recently been discussing what resources should be included in the Resource Timing arrays.  This email is intended to explain our current thoughts and solicit feedback and concerns.

Here's a summary of the thoughts and constraints we face when considering what resources to include in Resource Timing:

*         Take inspiration from, and expose the same information to the web developer as in the Net panels in Firebug/Chrome/IE

*         Provide a complete list of resources that were download, including same-origin and cross-origin

*         Respect the privacy of the user by not explicitly exposing additional information about what sites they've previously visited (via timing attacks targeting content that might be in their cache)

*         Protect the security of cross-origin servers and their resources

Below is a small example enumerating the resources that would be included in Resource Timing, starting from a sample HTML page.  Our proposal is to capture all of the "download-able" resources (from the browser's perspective), as opposed to the actual "download-ed" resources.  If we only exposed the download-ed resources, and not items that are already in the cache, the absence of the resource in Resource Timing gives definitive knowledge that the resource is already in the user's cache, which wouldn't be respecting their privacy (especially for content on other domains).

Note: This email also exposes some of our thoughts regarding what to do about resources from non-same-origin domains, but does not go into details.  Jatinder will follow up with our thoughts on how we can address resources loaded from non-same-origin domains.

Here's a sample HTML page to explain our thoughts.  I've highlighted the resources that would be included in Resource Timing, and go into details below:

<!DOCTYPE html>
<html>
    <head></head>
    <body>
        <img src="1.jpg" id="1a" />
        <img src="1.jpg" id="1b" />
        <img src="1.jpg" id="1c" />
        <img src="2.jpg" id="2" />
        <img src="http://otherdomain.com/3.jpg" id="3" />
        <img src="4.jpg" id="4" /> <!-- already in cache -->
        <img src="http://otherdomain.com/5.jpg" id="5" /> <!-- already in cache -->
        <img src="6.jpg" id="6" /> <!-- redirects to 6b.jpg -->
        <script>
            xmlHttp = new XMLHttpRequest();
            xmlHttp.open("GET", "xhr-get-cacheable.xml");
            xmlHttp.send(null); // resource is cacheable

            xmlHttp = new XMLHttpRequest();
            xmlHttp.open("GET", "xhr-get-cacheable.xml");
            xmlHttp.send(null);

            xmlHttp = new XMLHttpRequest();
            xmlHttp.open("GET", "xhr-get-no-cache.xml");
            xmlHttp.send(null); // resource is not cacheable

            xmlHttp = new XMLHttpRequest();
            xmlHttp.open("GET", "xhr-get-no-cache.xml");
            xmlHttp.send(null);

            var rt = performance.getResourceTimings();
        </script>
    </body>
</html>

With these assumptions:

*         The page is loading from mydomain.com.  Two resources (id="3" and id="5") are loading from otherdomain.com

*         The browser cache is empty except for 4.jpg on mydomain.com, and 5.jpg on otherdomain.com.  Neither of these cached resources require network revalidation.

*         All responses are HTTP 200, except 6.jpg which 301 redirects to 6b.jpg

Notes about each specific resource from the sample HTML page:

*         id="1a": Included, because this is the browser's first request for 1.jpg.  All timestamps filled out as normal.  We associate id="1a" because it was the first initiator of the request for 1.jpg.

*         id="1b": Not included, because the browser already initiated a download via id="1a" and won't initiate a second download.

*         id="1c": Not included, because the browser already initiated a download via id="1a" and won't initiate a second download.

*         id="2": Included, because this was the browser's first request for 2.jpg.  All timestamps filled out as normal.  This re-uses an existing connection so its domainLookup and connectStart times are the same as fetchStart (as an example).

*         id="3": Included, because this was the browser's first request for 3.jpg.  On a second domain, so the only timestamps available are fetchStart, loadEventStart and loadEventEnd.  All others are zero'd.

*         id="4": Included, because this was the browser's first request for 4.jpg.  Was in the cache, so its times from fetchStart through loadEventStart are all 41 (no elapsed time).  This exposes the same precision as attaching a load handler to the img and timing the elapsed time.

*         id="5": Included, because this was the browser's first request for 5.jpg.  On a second domain, so the only timestamps available are fetchStart, loadEventStart and loadEventEnd.  All others are zero'd.  Was in the cache, so the time between fetchStart and loadEventStart are very close but exposes the same precision as attaching a load handler.

*         id="6": Included, because this was the browser's first request for 6.jpg.  All timestamps filled out as normal.  Was redirected to 6b.jpg, but its "url" attribute is the original URL as the developer specified.

*         1st xhr-get-cacheable.xml (XHR is cacheable): Included

*         2nd xhr-get-cacheable.xml (XHR is cacheable): Not included as the browser re-uses the resource

*         1st and 2nd xhr-get-no-cache.xml (XHR is set to Cache-Control: no-cache): Both are included as the browser gets the resource twice.

This is an example of getResourceTiming() after this page load.  UTC timestamps are simplified on a timeline from 0 to 110.  For reference, resourceType 4 = RESOURCE_IMAGE and 8 = RESOURCE_XMLHTTPREQUEST:

[{
    "url":"http://mydomain.com/1.jpg",
    "resourceType": 4,
    "id": "1a",
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 10,
    "domainLookupStart": 10,
    "domainLookupEnd": 11,
    "connectStart": 12,
    "connectEnd": 13,
    "requestStart": 13,
    "requestEnd": 14,
    "responseStart": 14,
    "responseEnd": 15,
    "loadEventStart": 16,
    "loadEventEnd": 17
},{
    "url":"http://mydomain.com/2.jpg",
    "resourceType": 4,
    "id": "2"
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 20,
    "domainLookupStart": 21,
    "domainLookupEnd": 21,
    "connectStart": 21,
    "connectEnd": 21,
    "requestStart": 23,
    "requestEnd": 24,
    "responseStart": 24,
    "responseEnd": 25,
    "loadEventStart": 26,
    "loadEventEnd": 27
},{
    "url":"http://otherdomain.com/3.jpg",
    "resourceType": 4,
    "id": "3"
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 31,
    "domainLookupStart": 0,
    "domainLookupEnd": 0,
    "connectStart": 0,
    "connectEnd": 0,
    "requestStart": 0,
    "requestEnd": 0,
    "responseStart": 0,
    "responseEnd": 0,
    "loadEventStart": 36,
    "loadEventEnd": 37
},{
    "url":"http://mydomain.com/4.jpg",
    "resourceType": 4,
    "id": "4"
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 41,
    "domainLookupStart": 41,
    "domainLookupEnd": 41,
    "connectStart": 41,
    "connectEnd": 41,
    "requestStart": 41,
    "requestEnd": 41,
    "responseStart": 41,
    "responseEnd": 41,
    "loadEventStart": 42,
    "loadEventEnd": 42
},{
    "url":"http://otherdomain.com/5.jpg",
    "resourceType": 4,
    "id": "5"
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 51,
    "domainLookupStart": 0,
    "domainLookupEnd": 0,
    "connectStart": 0,
    "connectEnd": 0,
    "requestStart": 0,
    "requestEnd": 0,
    "responseStart": 0,
    "responseEnd": 0,
    "loadEventStart": 52,
    "loadEventEnd": 52
},{
    "url":"http://mydomain.com/6.jpg",
    "resourceType": 4,
    "id": "6",
    "redirectStart": 61,
    "redirectEnd": 70,
    "fetchStart": 70,
    "domainLookupStart": 71,
    "domainLookupEnd": 71,
    "connectStart": 72,
    "connectEnd": 73,
    "requestStart": 73,
    "requestEnd": 74,
    "responseStart": 74,
    "responseEnd": 75,
    "loadEventStart": 76,
    "loadEventEnd": 77
},{
    "url":"http://mydomain.com/xhr-get-cacheable.xml",
    "resourceType": 8,
    "id": ""
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 81,
    "domainLookupStart": 81,
    "domainLookupEnd": 81,
    "connectStart": 82,
    "connectEnd": 83,
    "requestStart": 83,
    "requestEnd": 84,
    "responseStart": 84,
    "responseEnd": 85,
    "loadEventStart": 86,
    "loadEventEnd": 87
},{
    "url":"http://mydomain.com/xhr-get-no-cache.xml",
    "resourceType": 8,
    "id": ""
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 91,
    "domainLookupStart": 91,
    "domainLookupEnd": 91,
    "connectStart": 92,
    "connectEnd": 93,
    "requestStart": 93,
    "requestEnd": 94,
    "responseStart": 94,
    "responseEnd": 95,
    "loadEventStart": 96,
    "loadEventEnd": 97
},{
    "url":"http://mydomain.com/xhr-get-no-cache.xml",
    "resourceType": 8,
    "id": ""
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 100,
    "domainLookupStart": 101,
    "domainLookupEnd": 101,
    "connectStart": 102,
    "connectEnd": 103,
    "requestStart": 103,
    "requestEnd": 104,
    "responseStart": 104,
    "responseEnd": 105,
    "loadEventStart": 106,
    "loadEventEnd": 107
}]

- Nic

Received on Tuesday, 22 March 2011 23:56:15 UTC