254 lines
12 KiB
JavaScript
254 lines
12 KiB
JavaScript
/*
|
|
* Copyright (C) 2017 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.
|
|
*/
|
|
|
|
WI.ResourceSizesContentView = class ResourceSizesContentView extends WI.ContentView
|
|
{
|
|
constructor(resource, delegate)
|
|
{
|
|
super(null);
|
|
|
|
console.assert(resource instanceof WI.Resource);
|
|
console.assert(delegate);
|
|
|
|
this._resource = resource;
|
|
this._resource.addEventListener(WI.Resource.Event.SizeDidChange, this._resourceSizeDidChange, this);
|
|
this._resource.addEventListener(WI.Resource.Event.TransferSizeDidChange, this._resourceTransferSizeDidChange, this);
|
|
this._resource.addEventListener(WI.Resource.Event.MetricsDidChange, this._resourceMetricsDidChange, this);
|
|
|
|
this._delegate = delegate;
|
|
|
|
this.element.classList.add("resource-details", "resource-sizes");
|
|
|
|
this._needsTransferSizesRefresh = false;
|
|
this._needsResourceSizeRefresh = false;
|
|
}
|
|
|
|
// Protected
|
|
|
|
initialLayout()
|
|
{
|
|
super.initialLayout();
|
|
|
|
let contentElement = this.element.appendChild(document.createElement("div"));
|
|
contentElement.className = "content";
|
|
|
|
let networkSection = contentElement.appendChild(document.createElement("section"));
|
|
networkSection.className = "network split";
|
|
|
|
function createSizeComponents(parentElement, subtitle, imageSource, label1, label2) {
|
|
let subtitleElement = parentElement.appendChild(document.createElement("div"));
|
|
subtitleElement.className = "subtitle";
|
|
subtitleElement.textContent = subtitle;
|
|
|
|
let container = parentElement.appendChild(document.createElement("div"));
|
|
container.className = "container";
|
|
|
|
let imageElement = container.appendChild(document.createElement("img"));
|
|
if (imageSource)
|
|
imageElement.src = imageSource;
|
|
|
|
let bytesElement = container.appendChild(document.createElement("div"));
|
|
bytesElement.className = "bytes";
|
|
|
|
let table = parentElement.appendChild(document.createElement("table"));
|
|
let headerRow = table.appendChild(document.createElement("tr"));
|
|
let label1Element = headerRow.appendChild(document.createElement("td"));
|
|
let value1Element = headerRow.appendChild(document.createElement("td"));
|
|
let bodyRow = table.appendChild(document.createElement("tr"));
|
|
let label2Element = bodyRow.appendChild(document.createElement("td"));
|
|
let value2Element = bodyRow.appendChild(document.createElement("td"));
|
|
|
|
label1Element.textContent = label1;
|
|
label1Element.className = "label";
|
|
label2Element.textContent = label2;
|
|
label2Element.className = "label";
|
|
|
|
return {
|
|
container,
|
|
bytesElement,
|
|
imageElement,
|
|
value1Element,
|
|
value2Element,
|
|
};
|
|
}
|
|
|
|
let sendingSection = networkSection.appendChild(document.createElement("div"));
|
|
sendingSection.className = "subsection";
|
|
|
|
let sendingComponents = createSizeComponents(sendingSection, WI.UIString("Bytes Sent"), "Images/Sending.svg", WI.UIString("Headers:"), WI.UIString("Body:"));
|
|
this._sendingBytesElement = sendingComponents.bytesElement;
|
|
this._sendingHeaderBytesElement = sendingComponents.value1Element;
|
|
this._sendingBodyBytesElement = sendingComponents.value2Element;
|
|
|
|
let bytesDivider = networkSection.appendChild(document.createElement("div"));
|
|
bytesDivider.className = "divider";
|
|
|
|
let receivingSection = networkSection.appendChild(document.createElement("div"));
|
|
receivingSection.className = "subsection";
|
|
|
|
let receivingComponents = createSizeComponents(receivingSection, WI.UIString("Bytes Received"), "Images/Receiving.svg", WI.UIString("Headers:"), WI.UIString("Body:"));
|
|
this._receivingBytesElement = receivingComponents.bytesElement;
|
|
this._receivingHeaderBytesElement = receivingComponents.value1Element;
|
|
this._receivingBodyBytesElement = receivingComponents.value2Element;
|
|
|
|
let resourceDivider = networkSection.appendChild(document.createElement("div"));
|
|
resourceDivider.className = "divider";
|
|
|
|
let resourceSection = networkSection.appendChild(document.createElement("div"));
|
|
resourceSection.className = "subsection large";
|
|
|
|
let resourceComponents = createSizeComponents(resourceSection, WI.UIString("Resource Size"), null, WI.UIString("Compression:"), WI.UIString("MIME Type:"));
|
|
resourceComponents.container.classList.add(WI.ResourceTreeElement.ResourceIconStyleClassName, ...WI.Resource.classNamesForResource(this._resource));
|
|
|
|
resourceComponents.imageElement.classList.add("icon");
|
|
if (this._resource.responseSource === WI.Resource.ResponseSource.InspectorOverride)
|
|
resourceComponents.imageElement.title = WI.UIString("This resource was loaded from a local override");
|
|
|
|
this._resourceBytesElement = resourceComponents.bytesElement;
|
|
this._compressionElement = resourceComponents.value1Element;
|
|
this._contentTypeElement = resourceComponents.value2Element;
|
|
|
|
this._refreshTransferSizeSections();
|
|
this._refreshResourceSizeSection();
|
|
|
|
this._needsTransferSizesRefresh = false;
|
|
this._needsResourceSizeRefresh = false;
|
|
}
|
|
|
|
layout()
|
|
{
|
|
super.layout();
|
|
|
|
if (this._needsTransferSizesRefresh) {
|
|
this._refreshTransferSizeSections();
|
|
this._needsTransferSizesRefresh = false;
|
|
}
|
|
|
|
if (this._needsResourceSizeRefresh) {
|
|
this._refreshResourceSizeSection();
|
|
this._needsResourceSizeRefresh = false;
|
|
}
|
|
}
|
|
|
|
closed()
|
|
{
|
|
this._resource.removeEventListener(WI.Resource.Event.SizeDidChange, this._resourceSizeDidChange, this);
|
|
this._resource.removeEventListener(WI.Resource.Event.TransferSizeDidChange, this._resourceTransferSizeDidChange, this);
|
|
this._resource.removeEventListener(WI.Resource.Event.MetricsDidChange, this._resourceMetricsDidChange, this);
|
|
|
|
super.closed();
|
|
}
|
|
|
|
// Private
|
|
|
|
_formattedSizeComponent(bytes)
|
|
{
|
|
console.assert(bytes >= 0);
|
|
|
|
// Prefer KB over B. And prefer 1 decimal point to keep sizes simple
|
|
// but we will still need B if bytes is less than 0.1 KB (100 B).
|
|
const higherResolution = false;
|
|
const bytesThreshold = 100;
|
|
return Number.bytesToString(bytes, higherResolution, bytesThreshold);
|
|
}
|
|
|
|
_refreshTransferSizeSections()
|
|
{
|
|
let bytesSentHeader = this._resource.requestHeadersTransferSize;
|
|
let bytesSentBody = this._resource.requestBodyTransferSize;
|
|
let bytesSent = bytesSentHeader + bytesSentBody;
|
|
|
|
let bytesReceivedHeader = this._resource.responseHeadersTransferSize;
|
|
let bytesReceivedBody = this._resource.responseBodyTransferSize;
|
|
let bytesReceived = bytesReceivedHeader + bytesReceivedBody;
|
|
|
|
this._sendingBytesElement.textContent = this._formattedSizeComponent(bytesSent || 0);
|
|
|
|
this._sendingHeaderBytesElement.textContent = bytesSentHeader ? Number.bytesToString(bytesSentHeader) : emDash;
|
|
this._sendingBodyBytesElement.textContent = bytesSentBody ? Number.bytesToString(bytesSentBody) : emDash;
|
|
|
|
this._receivingBytesElement.textContent = this._formattedSizeComponent(bytesReceived || 0);
|
|
|
|
this._receivingHeaderBytesElement.textContent = bytesReceivedHeader ? Number.bytesToString(bytesReceivedHeader) : emDash;
|
|
this._receivingBodyBytesElement.textContent = bytesReceivedBody ? Number.bytesToString(bytesReceivedBody) : emDash;
|
|
|
|
function appendGoToArrow(parentElement, handler) {
|
|
let goToButton = parentElement.appendChild(WI.createGoToArrowButton());
|
|
goToButton.addEventListener("click", handler);
|
|
}
|
|
|
|
if (bytesSentHeader)
|
|
appendGoToArrow(this._sendingHeaderBytesElement, () => { this._delegate.sizesContentViewGoToHeaders(this); });
|
|
if (bytesSentBody)
|
|
appendGoToArrow(this._sendingBodyBytesElement, () => { this._delegate.sizesContentViewGoToRequestBody(this); });
|
|
if (bytesReceivedHeader)
|
|
appendGoToArrow(this._receivingHeaderBytesElement, () => { this._delegate.sizesContentViewGoToHeaders(this); });
|
|
if (bytesReceivedBody)
|
|
appendGoToArrow(this._receivingBodyBytesElement, () => { this._delegate.sizesContentViewGoToResponseBody(this); });
|
|
}
|
|
|
|
_refreshResourceSizeSection()
|
|
{
|
|
let encodedSize = !isNaN(this._resource.networkEncodedSize) ? this._resource.networkEncodedSize : this._resource.estimatedNetworkEncodedSize;
|
|
let decodedSize = !isNaN(this._resource.networkDecodedSize) ? this._resource.networkDecodedSize : this._resource.size;
|
|
let compressionRate = decodedSize / encodedSize;
|
|
let compressionString = compressionRate > 0 && isFinite(compressionRate) ? WI.UIString("%.2f\u00d7").format(compressionRate) : WI.UIString("None");
|
|
|
|
this._resourceBytesElement.textContent = this._formattedSizeComponent(decodedSize || 0);
|
|
|
|
let contentEncoding = this._resource.responseHeaders.valueForCaseInsensitiveKey("Content-Encoding");
|
|
if (contentEncoding)
|
|
compressionString += ` (${contentEncoding.toLowerCase()})`;
|
|
|
|
this._compressionElement.textContent = compressionString;
|
|
this._contentTypeElement.textContent = this._resource.mimeType || emDash;
|
|
|
|
const minimumSizeBeforeWarning = 1024;
|
|
if (compressionRate <= 1 && encodedSize >= minimumSizeBeforeWarning && WI.shouldTreatMIMETypeAsText(this._resource.mimeType))
|
|
this._compressionElement.appendChild(WI.ImageUtilities.useSVGSymbol("Images/Warning.svg", "warning", WI.UIString("This text resource could benefit from compression")));
|
|
}
|
|
|
|
_resourceSizeDidChange(event)
|
|
{
|
|
this._needsTransferSizesRefresh = true;
|
|
this.needsLayout();
|
|
}
|
|
|
|
_resourceTransferSizeDidChange(event)
|
|
{
|
|
this._needsTransferSizesRefresh = true;
|
|
this.needsLayout();
|
|
}
|
|
|
|
_resourceMetricsDidChange(event)
|
|
{
|
|
this._needsTransferSizesRefresh = true;
|
|
this._needsResourceSizeRefresh = true;
|
|
this.needsLayout();
|
|
}
|
|
};
|
|
|
|
WI.ResourceSizesContentView.ReferencePage = WI.ReferencePage.NetworkTab.SizesPane;
|