Diff
Modified: trunk/LayoutTests/ChangeLog (205210 => 205211)
--- trunk/LayoutTests/ChangeLog 2016-08-31 00:15:50 UTC (rev 205210)
+++ trunk/LayoutTests/ChangeLog 2016-08-31 00:27:27 UTC (rev 205211)
@@ -1,3 +1,15 @@
+2016-08-30 Johan K. Jensen <[email protected]>
+
+ Web Inspector: Add resource timing model with timing information
+ https://bugs.webkit.org/show_bug.cgi?id=161314
+
+ Reviewed by Joseph Pecoraro.
+
+ Add tests for the Resource Timing Data model.
+
+ * http/tests/inspector/network/resource-timing-expected.txt: Added.
+ * http/tests/inspector/network/resource-timing.html: Added.
+
2016-08-30 Chris Dumez <[email protected]>
Object.setPrototypeOf() should throw when used on a cross-origin Window / Location object
Added: trunk/LayoutTests/http/tests/inspector/network/resource-timing-expected.txt (0 => 205211)
--- trunk/LayoutTests/http/tests/inspector/network/resource-timing-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/inspector/network/resource-timing-expected.txt 2016-08-31 00:27:27 UTC (rev 205211)
@@ -0,0 +1,19 @@
+Tests that a resource has timing information.
+
+
+== Running test suite: ResourceTimingData
+-- Running test case: CheckResourceTimingInformationForResource
+PASS: Newly added resource should have a resource timing model.
+PASS: Newly added resource should have a start time.
+PASS: Resource should now contain timing information.
+PASS: Resource should have a start time.
+PASS: Resource should have a request start time.
+PASS: Resource should have a response start time.
+PASS: domainLookupStart and domainLookupEnd should both be NaN or a number.
+PASS: connectStart and connectEnd should both be NaN or a number.
+PASS: requestStart should come after startTime.
+PASS: A secure connection should be reused or secureConnectionStart should come after connectStart.
+PASS: responseStart should come after requestStart.
+PASS: responseEnd should not be available yet.
+PASS: responseEnd should come after responseStart.
+
Added: trunk/LayoutTests/http/tests/inspector/network/resource-timing.html (0 => 205211)
--- trunk/LayoutTests/http/tests/inspector/network/resource-timing.html (rev 0)
+++ trunk/LayoutTests/http/tests/inspector/network/resource-timing.html 2016-08-31 00:27:27 UTC (rev 205211)
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src=""
+<script>
+function createRequest() {
+ let img = document.createElement("img");
+ img.src = ""
+ document.body.appendChild(img);
+}
+
+function test() {
+ let suite = InspectorTest.createAsyncSuite("ResourceTimingData");
+ InspectorTest.debug();
+ suite.addTestCase({
+ name: "CheckResourceTimingInformationForResource",
+ description: "Check if a resource has timing information.",
+ test: (resolve, reject) => {
+ InspectorTest.evaluateInPage("createRequest()");
+ WebInspector.Frame.singleFireEventListener(WebInspector.Frame.Event.ResourceWasAdded, (event) => {
+ let resource = event.data.resource;
+
+ InspectorTest.expectThat(resource.timingData instanceof WebInspector.ResourceTimingData, "Newly added resource should have a resource timing model.");
+ InspectorTest.expectThat(resource.timingData.startTime, "Newly added resource should have a start time.");
+
+ resource.singleFireEventListener(WebInspector.Resource.Event.ResponseReceived, (event) => {
+ let timingData = resource.timingData;
+
+ InspectorTest.expectThat(timingData, "Resource should now contain timing information.");
+ InspectorTest.expectThat(timingData.startTime > 0, "Resource should have a start time.");
+ InspectorTest.expectThat(timingData.requestStart > 0, "Resource should have a request start time.");
+ InspectorTest.expectThat(timingData.responseStart > 0, "Resource should have a response start time.");
+
+ InspectorTest.expectThat(typeof timingData.domainLookupStart === "number" && typeof timingData.domainLookupEnd === "number", "domainLookupStart and domainLookupEnd should both be NaN or a number.");
+ InspectorTest.expectThat(typeof timingData.connectStart === "number" && typeof timingData.connectStart === "number", "connectStart and connectEnd should both be NaN or a number.");
+
+ InspectorTest.expectThat(timingData.startTime <= timingData.requestStart, "requestStart should come after startTime.");
+ InspectorTest.expectThat(isNaN(timingData.secureConnectionStart) || timingData.connectStart <= timingData.secureConnectionStart, "A secure connection should be reused or secureConnectionStart should come after connectStart.");
+ InspectorTest.expectThat(timingData.requestStart <= timingData.responseStart, "responseStart should come after requestStart.");
+ InspectorTest.expectThat(isNaN(timingData.responseEnd), "responseEnd should not be available yet.");
+ });
+
+ resource.singleFireEventListener(WebInspector.Resource.Event.LoadingDidFinish, (event) => {
+ let timingData = resource.timingData;
+ InspectorTest.expectThat(timingData.responseStart <= timingData.responseEnd, "responseEnd should come after responseStart.");
+ resolve();
+ });
+ });
+ }
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+ <p>Tests that a resource has timing information.</p>
+</body>
+</html>
Modified: trunk/Source/WebInspectorUI/ChangeLog (205210 => 205211)
--- trunk/Source/WebInspectorUI/ChangeLog 2016-08-31 00:15:50 UTC (rev 205210)
+++ trunk/Source/WebInspectorUI/ChangeLog 2016-08-31 00:27:27 UTC (rev 205211)
@@ -1,3 +1,60 @@
+2016-08-30 Johan K. Jensen <[email protected]>
+
+ Web Inspector: Add resource timing model with timing information
+ https://bugs.webkit.org/show_bug.cgi?id=161314
+
+ Reviewed by Joseph Pecoraro.
+
+ Add a resource timing data model and populate it with info from the
+ response from the backend.
+
+ * UserInterface/Controllers/FrameResourceManager.js:
+ (WebInspector.FrameResourceManager.prototype.resourceRequestWasServedFromMemoryCache):
+ (WebInspector.FrameResourceManager.prototype.resourceRequestDidReceiveResponse):
+ Forward timing data from response to Resource.js.
+
+ * UserInterface/Main.html: Add new ResourceTimingData.js.
+ * UserInterface/Test.html: Add new ResourceTimingData.js.
+
+ * UserInterface/Models/Resource.js:
+ (WebInspector.Resource): Instantiate ResourceTimingData object.
+
+ (WebInspector.Resource.prototype.get timing):
+ (WebInspector.Resource.prototype.get firstTimestamp):
+ (WebInspector.Resource.prototype.get lastTimestamp):
+ (WebInspector.Resource.prototype.get duration):
+ (WebInspector.Resource.prototype.get latency):
+ (WebInspector.Resource.prototype.get receiveDuration):
+ Update getters to use new timing model.
+
+ (WebInspector.Resource.prototype.updateForResponse):
+ Update timing object with info from response.
+
+ (WebInspector.Resource.prototype.markAsFinished):
+ Log response end time.
+
+ * UserInterface/Models/ResourceTimelineRecord.js:
+ (WebInspector.ResourceTimelineRecord.prototype.get startTime):
+ (WebInspector.ResourceTimelineRecord.prototype.get activeStartTime):
+ (WebInspector.ResourceTimelineRecord.prototype.get endTime):
+ Update getters to use new timing model.
+
+ * UserInterface/Models/ResourceTimingData.js: Added.
+ (WebInspector.ResourceTimingData):
+ (WebInspector.ResourceTimingData.fromPayload):
+ (WebInspector.ResourceTimingData.prototype.get startTime):
+ (WebInspector.ResourceTimingData.prototype.get domainLookupStart):
+ (WebInspector.ResourceTimingData.prototype.get domainLookupEnd):
+ (WebInspector.ResourceTimingData.prototype.get connectStart):
+ (WebInspector.ResourceTimingData.prototype.get connectEnd):
+ (WebInspector.ResourceTimingData.prototype.get secureConnectionStart):
+ (WebInspector.ResourceTimingData.prototype.get requestStart):
+ (WebInspector.ResourceTimingData.prototype.get responseStart):
+ (WebInspector.ResourceTimingData.prototype.get responseEnd):
+ (WebInspector.ResourceTimingData.prototype.markResponseEndTime):
+ Add new ResourceTimingData model and fall back on old timestamps
+ for when data is unavailable.
+
2016-08-30 Alex Christensen <[email protected]>
Fix WebInspectorUI in internal Windows build
Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/FrameResourceManager.js (205210 => 205211)
--- trunk/Source/WebInspectorUI/UserInterface/Controllers/FrameResourceManager.js 2016-08-31 00:15:50 UTC (rev 205210)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/FrameResourceManager.js 2016-08-31 00:27:27 UTC (rev 205211)
@@ -236,7 +236,7 @@
var response = cachedResourcePayload.response;
var resource = this._addNewResourceToFrame(requestIdentifier, frameIdentifier, loaderIdentifier, cachedResourcePayload.url, cachedResourcePayload.type, "GET", null, null, elapsedTime, null, null, initiatorSourceCodeLocation);
resource.markAsCached();
- resource.updateForResponse(cachedResourcePayload.url, response.mimeType, cachedResourcePayload.type, response.headers, response.status, response.statusText, elapsedTime);
+ resource.updateForResponse(cachedResourcePayload.url, response.mimeType, cachedResourcePayload.type, response.headers, response.status, response.statusText, elapsedTime, response.timing);
resource.increaseSize(cachedResourcePayload.bodySize, elapsedTime);
resource.increaseTransferSize(cachedResourcePayload.bodySize);
resource.markAsFinished(elapsedTime);
@@ -288,7 +288,7 @@
if (response.fromDiskCache)
resource.markAsCached();
- resource.updateForResponse(response.url, response.mimeType, type, response.headers, response.status, response.statusText, elapsedTime);
+ resource.updateForResponse(response.url, response.mimeType, type, response.headers, response.status, response.statusText, elapsedTime, response.timing);
}
resourceRequestDidReceiveData(requestIdentifier, dataLength, encodedDataLength, timestamp)
Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (205210 => 205211)
--- trunk/Source/WebInspectorUI/UserInterface/Main.html 2016-08-31 00:15:50 UTC (rev 205210)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html 2016-08-31 00:27:27 UTC (rev 205211)
@@ -357,6 +357,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js (205210 => 205211)
--- trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js 2016-08-31 00:15:50 UTC (rev 205210)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js 2016-08-31 00:27:27 UTC (rev 205211)
@@ -59,6 +59,7 @@
this._size = NaN;
this._transferSize = NaN;
this._cached = false;
+ this._timingData = new WebInspector.ResourceTimingData(this);
if (this._initiatorSourceCodeLocation && this._initiatorSourceCodeLocation.sourceCode instanceof WebInspector.Resource)
this._initiatorSourceCodeLocation.sourceCode.addInitiatedResource(this);
@@ -126,6 +127,8 @@
// Public
+ get timingData() { return this._timingData; }
+
get url()
{
return this._url;
@@ -317,27 +320,27 @@
get firstTimestamp()
{
- return this.requestSentTimestamp || this.lastRedirectReceivedTimestamp || this.responseReceivedTimestamp || this.lastDataReceivedTimestamp || this.finishedOrFailedTimestamp;
+ return this.timingData.startTime || this.lastRedirectReceivedTimestamp || this.responseReceivedTimestamp || this.lastDataReceivedTimestamp || this.finishedOrFailedTimestamp;
}
get lastTimestamp()
{
- return this.finishedOrFailedTimestamp || this.lastDataReceivedTimestamp || this.responseReceivedTimestamp || this.lastRedirectReceivedTimestamp || this.requestSentTimestamp;
+ return this.timingData.responseEnd || this.lastDataReceivedTimestamp || this.responseReceivedTimestamp || this.lastRedirectReceivedTimestamp || this.requestSentTimestamp;
}
get duration()
{
- return this._finishedOrFailedTimestamp - this._requestSentTimestamp;
+ return this.timingData.responseEnd - this.timingData.requestStart;
}
get latency()
{
- return this._responseReceivedTimestamp - this._requestSentTimestamp;
+ return this.timingData.responseStart - this.timingData.requestStart;
}
get receiveDuration()
{
- return this._finishedOrFailedTimestamp - this._responseReceivedTimestamp;
+ return this.timingData.responseEnd - this.timingData.responseStart;
}
get cached()
@@ -447,7 +450,7 @@
this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
}
- updateForResponse(url, mimeType, type, responseHeaders, statusCode, statusText, elapsedTime)
+ updateForResponse(url, mimeType, type, responseHeaders, statusCode, statusText, elapsedTime, timingData)
{
console.assert(!this._finished);
console.assert(!this._failed);
@@ -467,6 +470,7 @@
this._statusText = statusText;
this._responseHeaders = responseHeaders || {};
this._responseReceivedTimestamp = elapsedTime || NaN;
+ this._timingData = WebInspector.ResourceTimingData.fromPayload(timingData, this);
this._responseHeadersSize = String(this._statusCode).length + this._statusText.length + 12; // Extra length is for "HTTP/1.1 ", " ", and "\r\n".
for (var name in this._responseHeaders)
@@ -572,6 +576,7 @@
this._finished = true;
this._finishedOrFailedTimestamp = elapsedTime || NaN;
+ this._timingData.markResponseEndTime(elapsedTime || NaN);
if (this._finishThenRequestContentPromise)
this._finishThenRequestContentPromise = null;
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js (205210 => 205211)
--- trunk/Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js 2016-08-31 00:15:50 UTC (rev 205210)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/ResourceTimelineRecord.js 2016-08-31 00:27:27 UTC (rev 205211)
@@ -52,17 +52,17 @@
get startTime()
{
- return this._resource.requestSentTimestamp;
+ return this._resource.timingData.startTime;
}
get activeStartTime()
{
- return this._resource.responseReceivedTimestamp;
+ return this._resource.timingData.responseStart;
}
get endTime()
{
- return this._resource.finishedOrFailedTimestamp;
+ return this._resource.timingData.responseEnd;
}
// Private
Added: trunk/Source/WebInspectorUI/UserInterface/Models/ResourceTimingData.js (0 => 205211)
--- trunk/Source/WebInspectorUI/UserInterface/Models/ResourceTimingData.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/ResourceTimingData.js 2016-08-31 00:27:27 UTC (rev 205211)
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ResourceTimingData = class ResourceTimingData extends WebInspector.Object
+{
+ constructor(resource, data)
+ {
+ super();
+
+ data = "" || {};
+
+ console.assert(isNaN(data.domainLookupStart) === isNaN(data.domainLookupEnd));
+ console.assert(isNaN(data.connectStart) === isNaN(data.connectEnd));
+
+ this._resource = resource;
+
+ this._startTime = data.startTime || NaN;
+ this._domainLookupStart = data.domainLookupStart || NaN;
+ this._domainLookupEnd = data.domainLookupEnd || NaN;
+ this._connectStart = data.connectStart || NaN;
+ this._connectEnd = data.connectEnd || NaN;
+ this._secureConnectionStart = data.secureConnectionStart || NaN;
+ this._requestStart = data.requestStart || NaN;
+ this._responseStart = data.responseStart || NaN;
+ this._responseEnd = data.responseEnd || NaN;
+
+ if (this._domainLookupStart >= this._domainLookupEnd)
+ this._domainLookupStart = this._domainLookupEnd = NaN;
+
+ if (this._connectStart >= this._connectEnd)
+ this._connectStart = this._connectEnd = NaN;
+ }
+
+ // Static
+
+ static fromPayload(payload, resource)
+ {
+ payload = payload || {};
+
+ // COMPATIBILITY (iOS 10): Resource Timing data was incomplete and incorrect. Do not use it.
+ // iOS 7 sent a requestTime and iOS 8-9.3 sent a navigationStart time.
+ if (typeof payload.requestTime === "number" || typeof payload.navigationStart === "number")
+ payload = {};
+
+ function offsetToTimestamp(offset) {
+ return offset > 0 ? payload.startTime + offset / 1000 : NaN;
+ }
+
+ let data = {
+ startTime: payload.startTime,
+ domainLookupStart: offsetToTimestamp(payload.domainLookupStart),
+ domainLookupEnd: offsetToTimestamp(payload.domainLookupEnd),
+ connectStart: offsetToTimestamp(payload.connectStart),
+ connectEnd: offsetToTimestamp(payload.connectEnd),
+ secureConnectionStart: offsetToTimestamp(payload.secureConnectionStart),
+ requestStart: offsetToTimestamp(payload.requestStart),
+ responseStart: offsetToTimestamp(payload.responseStart),
+ responseEnd: offsetToTimestamp(payload.responseEnd)
+ };
+
+ // COMPATIBILITY (iOS 8): connectStart is zero if a secure connection is used.
+ if (isNaN(data.connectStart) && !isNaN(data.secureConnectionStart))
+ data.connectStart = data.secureConnectionStart;
+
+ return new WebInspector.ResourceTimingData(resource, data);
+ }
+
+ // Public
+
+ get startTime() { return this._startTime || this._resource.requestSentTimestamp; }
+ get domainLookupStart() { return this._domainLookupStart; }
+ get domainLookupEnd() { return this._domainLookupEnd; }
+ get connectStart() { return this._connectStart; }
+ get connectEnd() { return this._connectEnd; }
+ get secureConnectionStart() { return this._secureConnectionStart; }
+ get requestStart() { return this._requestStart || this._resource.requestSentTimestamp; }
+ get responseStart() { return this._responseStart || this._resource.responseReceivedTimestamp; }
+ get responseEnd() { return this._responseEnd || this._resource.finishedOrFailedTimestamp; }
+
+ markResponseEndTime(responseEnd)
+ {
+ console.assert(typeof responseEnd === "number");
+ this._responseEnd = responseEnd;
+ }
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Test.html (205210 => 205211)
--- trunk/Source/WebInspectorUI/UserInterface/Test.html 2016-08-31 00:15:50 UTC (rev 205210)
+++ trunk/Source/WebInspectorUI/UserInterface/Test.html 2016-08-31 00:27:27 UTC (rev 205211)
@@ -147,6 +147,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""