Added MacOS SDK
This commit is contained in:
@@ -0,0 +1,428 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 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.
|
||||
*/
|
||||
|
||||
// HTTP Archive (HAR) format - Version 1.2
|
||||
// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html#sec-har-object-types-creator
|
||||
// http://www.softwareishard.com/blog/har-12-spec/
|
||||
|
||||
WI.HARBuilder = class HARBuilder
|
||||
{
|
||||
static async buildArchive(resources)
|
||||
{
|
||||
let promises = [];
|
||||
for (let resource of resources) {
|
||||
console.assert(resource.finished);
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
// Always resolve.
|
||||
resource.requestContent().then(
|
||||
(x) => resolve(x),
|
||||
() => resolve(null)
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
let contents = await Promise.all(promises);
|
||||
console.assert(contents.length === resources.length);
|
||||
|
||||
return {
|
||||
log: {
|
||||
version: "1.2",
|
||||
creator: HARBuilder.creator(),
|
||||
pages: HARBuilder.pages(),
|
||||
entries: resources.map((resource, index) => HARBuilder.entry(resource, contents[index])),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static creator()
|
||||
{
|
||||
return {
|
||||
name: "WebKit Web Inspector",
|
||||
version: "1.0",
|
||||
};
|
||||
}
|
||||
|
||||
static pages()
|
||||
{
|
||||
return [{
|
||||
startedDateTime: HARBuilder.date(WI.networkManager.mainFrame.mainResource.requestSentDate),
|
||||
id: "page_0",
|
||||
title: WI.networkManager.mainFrame.url || "",
|
||||
pageTimings: HARBuilder.pageTimings(),
|
||||
}];
|
||||
}
|
||||
|
||||
static pageTimings()
|
||||
{
|
||||
let result = {};
|
||||
|
||||
let domContentReadyEventTimestamp = WI.networkManager.mainFrame.domContentReadyEventTimestamp;
|
||||
if (!isNaN(domContentReadyEventTimestamp))
|
||||
result.onContentLoad = domContentReadyEventTimestamp * 1000;
|
||||
|
||||
let loadEventTimestamp = WI.networkManager.mainFrame.loadEventTimestamp;
|
||||
if (!isNaN(loadEventTimestamp))
|
||||
result.onLoad = loadEventTimestamp * 1000;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static entry(resource, content)
|
||||
{
|
||||
let entry = {
|
||||
pageref: "page_0",
|
||||
startedDateTime: HARBuilder.date(resource.requestSentDate),
|
||||
time: 0,
|
||||
request: HARBuilder.request(resource),
|
||||
response: HARBuilder.response(resource, content),
|
||||
cache: HARBuilder.cache(resource),
|
||||
timings: HARBuilder.timings(resource),
|
||||
};
|
||||
|
||||
if (resource.timingData.startTime && resource.timingData.responseEnd)
|
||||
entry.time = (resource.timingData.responseEnd - resource.timingData.startTime) * 1000;
|
||||
if (resource.remoteAddress) {
|
||||
entry.serverIPAddress = HARBuilder.ipAddress(resource.remoteAddress);
|
||||
|
||||
// WebKit Custom Field `_serverPort`.
|
||||
if (entry.serverIPAddress)
|
||||
entry._serverPort = HARBuilder.port(resource.remoteAddress);
|
||||
}
|
||||
if (resource.connectionIdentifier)
|
||||
entry.connection = "" + resource.connectionIdentifier;
|
||||
|
||||
// CFNetwork Custom Field `_fetchType`.
|
||||
if (resource.responseSource !== WI.Resource.ResponseSource.Unknown)
|
||||
entry._fetchType = HARBuilder.fetchType(resource.responseSource);
|
||||
|
||||
// WebKit Custom Field `_priority`.
|
||||
if (resource.priority !== WI.Resource.NetworkPriority.Unknown)
|
||||
entry._priority = HARBuilder.priority(resource.priority);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static request(resource)
|
||||
{
|
||||
let result = {
|
||||
method: resource.requestMethod || "",
|
||||
url: resource.url || "",
|
||||
httpVersion: WI.Resource.displayNameForProtocol(resource.protocol) || "",
|
||||
cookies: HARBuilder.cookies(resource.requestCookies, null),
|
||||
headers: HARBuilder.headers(resource.requestHeaders),
|
||||
queryString: resource.queryStringParameters || [],
|
||||
headersSize: !isNaN(resource.requestHeadersTransferSize) ? resource.requestHeadersTransferSize : -1,
|
||||
bodySize: !isNaN(resource.requestBodyTransferSize) ? resource.requestBodyTransferSize : -1,
|
||||
};
|
||||
|
||||
if (resource.requestData)
|
||||
result.postData = HARBuilder.postData(resource);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static response(resource, content)
|
||||
{
|
||||
let result = {
|
||||
status: resource.statusCode || 0,
|
||||
statusText: resource.statusText || "",
|
||||
httpVersion: WI.Resource.displayNameForProtocol(resource.protocol) || "",
|
||||
cookies: HARBuilder.cookies(resource.responseCookies, resource.requestSentDate),
|
||||
headers: HARBuilder.headers(resource.responseHeaders),
|
||||
content: HARBuilder.content(resource, content),
|
||||
redirectURL: resource.responseHeaders.valueForCaseInsensitiveKey("Location") || "",
|
||||
headersSize: !isNaN(resource.responseHeadersTransferSize) ? resource.responseHeadersTransferSize : -1,
|
||||
bodySize: !isNaN(resource.responseBodyTransferSize) ? resource.responseBodyTransferSize : -1,
|
||||
};
|
||||
|
||||
// Chrome Custom Field `_transferSize`.
|
||||
if (!isNaN(resource.networkTotalTransferSize))
|
||||
result._transferSize = resource.networkTotalTransferSize;
|
||||
|
||||
// Chrome Custom Field `_error`.
|
||||
if (resource.failureReasonText)
|
||||
result._error = resource.failureReasonText;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static cookies(cookies, requestSentDate)
|
||||
{
|
||||
let result = [];
|
||||
|
||||
for (let cookie of cookies) {
|
||||
let json = {
|
||||
name: cookie.name,
|
||||
value: cookie.value,
|
||||
};
|
||||
|
||||
if (cookie.type === WI.Cookie.Type.Response) {
|
||||
if (cookie.path)
|
||||
json.path = cookie.path;
|
||||
if (cookie.domain)
|
||||
json.domain = cookie.domain;
|
||||
json.expires = HARBuilder.date(cookie.expirationDate(requestSentDate));
|
||||
json.httpOnly = cookie.httpOnly;
|
||||
json.secure = cookie.secure;
|
||||
if (cookie.sameSite !== WI.Cookie.SameSiteType.None)
|
||||
json.sameSite = cookie.sameSite;
|
||||
}
|
||||
|
||||
result.push(json);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static headers(headers)
|
||||
{
|
||||
let result = [];
|
||||
|
||||
for (let key in headers)
|
||||
result.push({name: key, value: headers[key]});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static content(resource, content)
|
||||
{
|
||||
let encodedSize = !isNaN(resource.networkEncodedSize) ? resource.networkEncodedSize : resource.estimatedNetworkEncodedSize;
|
||||
let decodedSize = !isNaN(resource.networkDecodedSize) ? resource.networkDecodedSize : resource.size;
|
||||
|
||||
if (isNaN(decodedSize))
|
||||
decodedSize = 0;
|
||||
if (isNaN(encodedSize))
|
||||
encodedSize = 0;
|
||||
|
||||
let result = {
|
||||
size: decodedSize,
|
||||
compression: decodedSize - encodedSize,
|
||||
mimeType: resource.mimeType || "x-unknown",
|
||||
};
|
||||
|
||||
if (content) {
|
||||
if (content.rawContent)
|
||||
result.text = content.rawContent;
|
||||
if (content.rawBase64Encoded)
|
||||
result.encoding = "base64";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static postData(resource)
|
||||
{
|
||||
return {
|
||||
mimeType: resource.requestDataContentType || "",
|
||||
text: resource.requestData,
|
||||
params: resource.requestFormParameters || [],
|
||||
};
|
||||
}
|
||||
|
||||
static cache(resource)
|
||||
{
|
||||
// FIXME: <https://webkit.org/b/178682> Web Inspector: Include <cache> details in HAR Export
|
||||
// http://www.softwareishard.com/blog/har-12-spec/#cache
|
||||
return {};
|
||||
}
|
||||
|
||||
static timings(resource)
|
||||
{
|
||||
// FIXME: <https://webkit.org/b/195694> Web Inspector: HAR Extension for Redirect Timing Info
|
||||
// Chrome has Custom Fields `_blocked_queueing` and `_blocked_proxy`.
|
||||
|
||||
let result = {
|
||||
blocked: -1,
|
||||
dns: -1,
|
||||
connect: -1,
|
||||
ssl: -1,
|
||||
send: 0,
|
||||
wait: 0,
|
||||
receive: 0,
|
||||
};
|
||||
|
||||
if (resource.timingData.startTime && resource.timingData.responseEnd) {
|
||||
let {startTime, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart, responseStart, responseEnd} = resource.timingData;
|
||||
result.blocked = ((domainLookupStart || connectStart || requestStart) - startTime) * 1000;
|
||||
if (domainLookupStart)
|
||||
result.dns = ((domainLookupEnd || connectStart || requestStart) - domainLookupStart) * 1000;
|
||||
if (connectStart)
|
||||
result.connect = ((connectEnd || requestStart) - connectStart) * 1000;
|
||||
if (secureConnectionStart)
|
||||
result.ssl = ((connectEnd || requestStart) - secureConnectionStart) * 1000;
|
||||
|
||||
// If all the time before requestStart was included in blocked, then make send time zero
|
||||
// as send time is essentially just blocked time after dns / connection time, and we
|
||||
// do not want to double count it.
|
||||
result.send = (domainLookupEnd || connectEnd) ? (requestStart - (connectEnd || domainLookupEnd)) * 1000 : 0;
|
||||
|
||||
result.wait = (responseStart - requestStart) * 1000;
|
||||
result.receive = (responseEnd - responseStart) * 1000;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
static ipAddress(remoteAddress)
|
||||
{
|
||||
// IP Address, without port.
|
||||
if (!remoteAddress)
|
||||
return "";
|
||||
|
||||
// NOTE: Resource.remoteAddress always includes the port at the end.
|
||||
// So this always strips the last part.
|
||||
return remoteAddress.replace(/:\d+$/, "");
|
||||
}
|
||||
|
||||
static port(remoteAddress)
|
||||
{
|
||||
// IP Address, without port.
|
||||
if (!remoteAddress)
|
||||
return undefined;
|
||||
|
||||
// NOTE: Resource.remoteAddress always includes the port at the end.
|
||||
// So this always matches the last part.
|
||||
let index = remoteAddress.lastIndexOf(":");
|
||||
if (!index)
|
||||
return undefined;
|
||||
|
||||
let portString = remoteAddress.substr(index + 1);
|
||||
let port = parseInt(portString);
|
||||
if (isNaN(port))
|
||||
return undefined;
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
static date(date)
|
||||
{
|
||||
// ISO 8601
|
||||
if (!date)
|
||||
return "";
|
||||
|
||||
return date.toISOString();
|
||||
}
|
||||
|
||||
static fetchType(responseSource)
|
||||
{
|
||||
switch (responseSource) {
|
||||
case WI.Resource.ResponseSource.Network:
|
||||
return "Network Load";
|
||||
case WI.Resource.ResponseSource.MemoryCache:
|
||||
return "Memory Cache";
|
||||
case WI.Resource.ResponseSource.DiskCache:
|
||||
return "Disk Cache";
|
||||
case WI.Resource.ResponseSource.ServiceWorker:
|
||||
return "Service Worker";
|
||||
case WI.Resource.ResponseSource.InspectorOverride:
|
||||
return "Inspector Override";
|
||||
}
|
||||
|
||||
console.assert();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static priority(priority)
|
||||
{
|
||||
switch (priority) {
|
||||
case WI.Resource.NetworkPriority.Low:
|
||||
return "low";
|
||||
case WI.Resource.NetworkPriority.Medium:
|
||||
return "medium";
|
||||
case WI.Resource.NetworkPriority.High:
|
||||
return "high";
|
||||
}
|
||||
|
||||
console.assert();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Consuming.
|
||||
|
||||
static dateFromHARDate(isoString)
|
||||
{
|
||||
return Date.parse(isoString);
|
||||
}
|
||||
|
||||
static protocolFromHARProtocol(protocol)
|
||||
{
|
||||
switch (protocol) {
|
||||
case "HTTP/2":
|
||||
return "h2";
|
||||
case "HTTP/1.0":
|
||||
return "http/1.0";
|
||||
case "HTTP/1.1":
|
||||
return "http/1.1";
|
||||
case "SPDY/2":
|
||||
return "spdy/2";
|
||||
case "SPDY/3":
|
||||
return "spdy/3";
|
||||
case "SPDY/3.1":
|
||||
return "spdy/3.1";
|
||||
}
|
||||
|
||||
if (protocol)
|
||||
console.warn("Unknown HAR protocol value", protocol);
|
||||
return null;
|
||||
}
|
||||
|
||||
static responseSourceFromHARFetchType(fetchType)
|
||||
{
|
||||
switch (fetchType) {
|
||||
case "Network Load":
|
||||
return WI.Resource.ResponseSource.Network;
|
||||
case "Memory Cache":
|
||||
return WI.Resource.ResponseSource.MemoryCache;
|
||||
case "Disk Cache":
|
||||
return WI.Resource.ResponseSource.DiskCache;
|
||||
case "Service Worker":
|
||||
return WI.Resource.ResponseSource.ServiceWorker;
|
||||
case "Inspector Override":
|
||||
return WI.Resource.ResponseSource.InspectorOverride;
|
||||
}
|
||||
|
||||
if (fetchType)
|
||||
console.warn("Unknown HAR _fetchType value", fetchType);
|
||||
return WI.Resource.ResponseSource.Other;
|
||||
}
|
||||
|
||||
static networkPriorityFromHARPriority(priority)
|
||||
{
|
||||
switch (priority) {
|
||||
case "low":
|
||||
return WI.Resource.NetworkPriority.Low;
|
||||
case "medium":
|
||||
return WI.Resource.NetworkPriority.Medium;
|
||||
case "high":
|
||||
return WI.Resource.NetworkPriority.High;
|
||||
}
|
||||
|
||||
if (priority)
|
||||
console.warn("Unknown HAR priority value", priority);
|
||||
return WI.Resource.NetworkPriority.Unknown;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user