summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/Views/ResourceDetailsSidebarPanel.js
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Views/ResourceDetailsSidebarPanel.js')
-rw-r--r--Source/WebInspectorUI/UserInterface/Views/ResourceDetailsSidebarPanel.js508
1 files changed, 508 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Views/ResourceDetailsSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/ResourceDetailsSidebarPanel.js
new file mode 100644
index 000000000..fe193f608
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Views/ResourceDetailsSidebarPanel.js
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2013, 2015 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.ResourceDetailsSidebarPanel = class ResourceDetailsSidebarPanel extends WebInspector.DetailsSidebarPanel
+{
+ constructor()
+ {
+ super("resource-details", WebInspector.UIString("Resource"), WebInspector.UIString("Resource"));
+
+ this.element.classList.add("resource");
+
+ this._resource = null;
+
+ this._typeMIMETypeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("MIME Type"));
+ this._typeResourceTypeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Resource Type"));
+
+ this._typeSection = new WebInspector.DetailsSection("resource-type", WebInspector.UIString("Type"));
+ this._typeSection.groups = [new WebInspector.DetailsSectionGroup([this._typeMIMETypeRow, this._typeResourceTypeRow])];
+
+ this._locationFullURLRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Full URL"));
+ this._locationSchemeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Scheme"));
+ this._locationHostRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Host"));
+ this._locationPortRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Port"));
+ this._locationPathRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Path"));
+ this._locationQueryStringRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Query String"));
+ this._locationFragmentRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Fragment"));
+ this._locationFilenameRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Filename"));
+ this._initiatorRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Initiator"));
+ this._initiatedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Initiated"));
+
+ var firstGroup = [this._locationFullURLRow];
+ var secondGroup = [this._locationSchemeRow, this._locationHostRow, this._locationPortRow, this._locationPathRow,
+ this._locationQueryStringRow, this._locationFragmentRow, this._locationFilenameRow];
+ var thirdGroup = [this._initiatorRow, this._initiatedRow];
+
+ this._fullURLGroup = new WebInspector.DetailsSectionGroup(firstGroup);
+ this._locationURLComponentsGroup = new WebInspector.DetailsSectionGroup(secondGroup);
+ this._relatedResourcesGroup = new WebInspector.DetailsSectionGroup(thirdGroup);
+
+ this._locationSection = new WebInspector.DetailsSection("resource-location", WebInspector.UIString("Location"), [this._fullURLGroup, this._locationURLComponentsGroup, this._relatedResourcesGroup]);
+
+ this._queryParametersRow = new WebInspector.DetailsSectionDataGridRow(null, WebInspector.UIString("No Query Parameters"));
+ this._queryParametersSection = new WebInspector.DetailsSection("resource-query-parameters", WebInspector.UIString("Query Parameters"));
+ this._queryParametersSection.groups = [new WebInspector.DetailsSectionGroup([this._queryParametersRow])];
+
+ this._requestDataSection = new WebInspector.DetailsSection("resource-request-data", WebInspector.UIString("Request Data"));
+
+ this._requestMethodRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Method"));
+ this._cachedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Cached"));
+
+ this._statusTextRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Status"));
+ this._statusCodeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Code"));
+
+ this._encodedSizeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Encoded"));
+ this._decodedSizeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Decoded"));
+ this._transferSizeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Transferred"));
+
+ this._compressedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Compressed"));
+ this._compressionRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Compression"));
+
+ var requestGroup = new WebInspector.DetailsSectionGroup([this._requestMethodRow, this._cachedRow]);
+ var statusGroup = new WebInspector.DetailsSectionGroup([this._statusTextRow, this._statusCodeRow]);
+ var sizeGroup = new WebInspector.DetailsSectionGroup([this._encodedSizeRow, this._decodedSizeRow, this._transferSizeRow]);
+ var compressionGroup = new WebInspector.DetailsSectionGroup([this._compressedRow, this._compressionRow]);
+
+ this._requestAndResponseSection = new WebInspector.DetailsSection("resource-request-response", WebInspector.UIString("Request & Response"), [requestGroup, statusGroup, sizeGroup, compressionGroup]);
+
+ this._requestHeadersRow = new WebInspector.DetailsSectionDataGridRow(null, WebInspector.UIString("No Request Headers"));
+ this._requestHeadersSection = new WebInspector.DetailsSection("resource-request-headers", WebInspector.UIString("Request Headers"));
+ this._requestHeadersSection.groups = [new WebInspector.DetailsSectionGroup([this._requestHeadersRow])];
+
+ this._responseHeadersRow = new WebInspector.DetailsSectionDataGridRow(null, WebInspector.UIString("No Response Headers"));
+ this._responseHeadersSection = new WebInspector.DetailsSection("resource-response-headers", WebInspector.UIString("Response Headers"));
+ this._responseHeadersSection.groups = [new WebInspector.DetailsSectionGroup([this._responseHeadersRow])];
+
+ // Rows for the "Image Size" section.
+ this._imageWidthRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Width"));
+ this._imageHeightRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Height"));
+
+ // "Image Size" section where we display intrinsic metrics for image resources.
+ this._imageSizeSection = new WebInspector.DetailsSection("resource-type", WebInspector.UIString("Image Size"));
+ this._imageSizeSection.groups = [new WebInspector.DetailsSectionGroup([this._imageWidthRow, this._imageHeightRow])];
+
+ this.contentView.element.appendChild(this._typeSection.element);
+ this.contentView.element.appendChild(this._locationSection.element);
+ this.contentView.element.appendChild(this._requestAndResponseSection.element);
+ this.contentView.element.appendChild(this._requestHeadersSection.element);
+ this.contentView.element.appendChild(this._responseHeadersSection.element);
+ }
+
+ // Public
+
+ inspect(objects)
+ {
+ // Convert to a single item array if needed.
+ if (!(objects instanceof Array))
+ objects = [objects];
+
+ var resourceToInspect = null;
+
+ // Iterate over the objects to find a WebInspector.Resource to inspect.
+ for (let object of objects) {
+ if (object instanceof WebInspector.Resource) {
+ resourceToInspect = object;
+ break;
+ }
+
+ if (object instanceof WebInspector.Frame) {
+ resourceToInspect = object.mainResource;
+ break;
+ }
+
+ // FIXME: <https://webkit.org/b/164427> Web Inspector: WorkerTarget's mainResource should be a Resource not a Script
+ // If that was the case, then we could just have WorkerTreeElement contain the Resource and not a Script.
+ if (object instanceof WebInspector.Script && object.isMainResource() && object.resource) {
+ resourceToInspect = object.resource;
+ break;
+ }
+ }
+
+ this.resource = resourceToInspect;
+
+ return !!this._resource;
+ }
+
+ get resource()
+ {
+ return this._resource;
+ }
+
+ set resource(resource)
+ {
+ if (resource === this._resource)
+ return;
+
+ if (this._resource) {
+ this._resource.removeEventListener(WebInspector.Resource.Event.URLDidChange, this._refreshURL, this);
+ this._resource.removeEventListener(WebInspector.Resource.Event.MIMETypeDidChange, this._refreshMIMEType, this);
+ this._resource.removeEventListener(WebInspector.Resource.Event.TypeDidChange, this._refreshResourceType, this);
+ this._resource.removeEventListener(WebInspector.Resource.Event.RequestHeadersDidChange, this._refreshRequestHeaders, this);
+ this._resource.removeEventListener(WebInspector.Resource.Event.ResponseReceived, this._refreshRequestAndResponse, this);
+ this._resource.removeEventListener(WebInspector.Resource.Event.CacheStatusDidChange, this._refreshRequestAndResponse, this);
+ this._resource.removeEventListener(WebInspector.Resource.Event.SizeDidChange, this._refreshDecodedSize, this);
+ this._resource.removeEventListener(WebInspector.Resource.Event.TransferSizeDidChange, this._refreshTransferSize, this);
+ this._resource.removeEventListener(WebInspector.Resource.Event.InitiatedResourcesDidChange, this._refreshRelatedResourcesSection, this);
+ }
+
+ this._resource = resource;
+
+ if (this._resource) {
+ this._resource.addEventListener(WebInspector.Resource.Event.URLDidChange, this._refreshURL, this);
+ this._resource.addEventListener(WebInspector.Resource.Event.MIMETypeDidChange, this._refreshMIMEType, this);
+ this._resource.addEventListener(WebInspector.Resource.Event.TypeDidChange, this._refreshResourceType, this);
+ this._resource.addEventListener(WebInspector.Resource.Event.RequestHeadersDidChange, this._refreshRequestHeaders, this);
+ this._resource.addEventListener(WebInspector.Resource.Event.ResponseReceived, this._refreshRequestAndResponse, this);
+ this._resource.addEventListener(WebInspector.Resource.Event.CacheStatusDidChange, this._refreshRequestAndResponse, this);
+ this._resource.addEventListener(WebInspector.Resource.Event.SizeDidChange, this._refreshDecodedSize, this);
+ this._resource.addEventListener(WebInspector.Resource.Event.TransferSizeDidChange, this._refreshTransferSize, this);
+ this._resource.addEventListener(WebInspector.Resource.Event.InitiatedResourcesDidChange, this._refreshRelatedResourcesSection, this);
+ }
+
+ this.needsLayout();
+ }
+
+ // Protected
+
+ layout()
+ {
+ if (!this._resource)
+ return;
+
+ this._refreshURL();
+ this._refreshMIMEType();
+ this._refreshResourceType();
+ this._refreshRequestAndResponse();
+ this._refreshDecodedSize();
+ this._refreshTransferSize();
+ this._refreshRequestHeaders();
+ this._refreshImageSizeSection();
+ this._refreshRequestDataSection();
+ this._refreshRelatedResourcesSection();
+ }
+
+ // Private
+
+ _refreshURL()
+ {
+ if (!this._resource)
+ return;
+
+ this._locationFullURLRow.value = this._resource.url.insertWordBreakCharacters();
+
+ var urlComponents = this._resource.urlComponents;
+ if (urlComponents.scheme) {
+ this._locationSection.groups = [this._fullURLGroup, this._locationURLComponentsGroup, this._relatedResourcesGroup];
+
+ this._locationSchemeRow.value = urlComponents.scheme ? urlComponents.scheme : null;
+ this._locationHostRow.value = urlComponents.host ? urlComponents.host : null;
+ this._locationPortRow.value = urlComponents.port ? urlComponents.port : null;
+ this._locationPathRow.value = urlComponents.path ? urlComponents.path.insertWordBreakCharacters() : null;
+ this._locationQueryStringRow.value = urlComponents.queryString ? urlComponents.queryString.insertWordBreakCharacters() : null;
+ this._locationFragmentRow.value = urlComponents.fragment ? urlComponents.fragment.insertWordBreakCharacters() : null;
+ this._locationFilenameRow.value = urlComponents.lastPathComponent ? urlComponents.lastPathComponent.insertWordBreakCharacters() : null;
+ } else {
+ this._locationSection.groups = [this._fullURLGroup, this._relatedResourcesGroup];
+ }
+
+ if (urlComponents.queryString) {
+ // Ensure the "Query Parameters" section is displayed, right after the "Request & Response" section.
+ this.contentView.element.insertBefore(this._queryParametersSection.element, this._requestAndResponseSection.element.nextSibling);
+
+ this._queryParametersRow.dataGrid = this._createNameValueDataGrid(parseQueryString(urlComponents.queryString, true));
+ } else {
+ // Hide the "Query Parameters" section if we don't have a query string.
+ var queryParametersSectionElement = this._queryParametersSection.element;
+ if (queryParametersSectionElement.parentNode)
+ queryParametersSectionElement.parentNode.removeChild(queryParametersSectionElement);
+ }
+ }
+
+ _refreshRelatedResourcesSection()
+ {
+ // Hide the section if we don't have anything to show.
+ let groups = this._locationSection.groups;
+ let isSectionVisible = groups.includes(this._relatedResourcesGroup);
+ if (!this._resource.initiatorSourceCodeLocation && !this._resource.initiatedResources.length) {
+ if (isSectionVisible) {
+ groups.remove(this._relatedResourcesGroup);
+ this._locationSection.groups = groups;
+ }
+ return;
+ }
+
+ if (!isSectionVisible) {
+ groups.push(this._relatedResourcesGroup);
+ this._locationSection.groups = groups;
+ }
+
+ let initiatorLocation = this._resource.initiatorSourceCodeLocation;
+ this._initiatorRow.value = initiatorLocation ? WebInspector.createSourceCodeLocationLink(initiatorLocation, true) : null;
+
+ let initiatedResources = this._resource.initiatedResources;
+ if (initiatedResources.length) {
+ let resourceLinkContainer = document.createElement("div");
+ for (let resource of initiatedResources)
+ resourceLinkContainer.appendChild(WebInspector.createResourceLink(resource));
+
+ this._initiatedRow.value = resourceLinkContainer;
+ } else
+ this._initiatedRow.value = null;
+ }
+
+ _refreshResourceType()
+ {
+ if (!this._resource)
+ return;
+
+ this._typeResourceTypeRow.value = WebInspector.Resource.displayNameForType(this._resource.type);
+ }
+
+ _refreshMIMEType()
+ {
+ if (!this._resource)
+ return;
+
+ this._typeMIMETypeRow.value = this._resource.mimeType;
+ }
+
+ _refreshRequestAndResponse()
+ {
+ var resource = this._resource;
+ if (!resource)
+ return;
+
+ // If we don't have a value, we set an em-dash to keep the row from hiding.
+ // This keeps the UI from shifting around as data comes in.
+
+ this._requestMethodRow.value = resource.requestMethod || emDash;
+
+ this._cachedRow.value = resource.cached ? WebInspector.UIString("Yes") : WebInspector.UIString("No");
+
+ this._statusCodeRow.value = resource.statusCode || emDash;
+ this._statusTextRow.value = resource.statusText || emDash;
+
+ this._refreshResponseHeaders();
+ this._refreshCompressed();
+ }
+
+ _valueForSize(size)
+ {
+ // If we don't have a value, we set an em-dash to keep the row from hiding.
+ // This keeps the UI from shifting around as data comes in.
+ return size > 0 ? Number.bytesToString(size) : emDash;
+ }
+
+ _refreshCompressed()
+ {
+ this._compressedRow.value = this._resource.compressed ? WebInspector.UIString("Yes") : WebInspector.UIString("No");
+ this._compressionRow.value = this._resource.compressed ? WebInspector.UIString("%.2f\u00d7").format(this._resource.size / this._resource.encodedSize) : null;
+ }
+
+ _refreshDecodedSize()
+ {
+ if (!this._resource)
+ return;
+
+ this._encodedSizeRow.value = this._valueForSize(this._resource.encodedSize);
+ this._decodedSizeRow.value = this._valueForSize(this._resource.size);
+
+ this._refreshCompressed();
+ }
+
+ _refreshTransferSize()
+ {
+ if (!this._resource)
+ return;
+
+ this._encodedSizeRow.value = this._valueForSize(this._resource.encodedSize);
+ this._transferSizeRow.value = this._valueForSize(this._resource.transferSize);
+
+ this._refreshCompressed();
+ }
+
+ _refreshRequestHeaders()
+ {
+ if (!this._resource)
+ return;
+
+ this._requestHeadersRow.dataGrid = this._createNameValueDataGrid(this._resource.requestHeaders);
+ }
+
+ _refreshResponseHeaders()
+ {
+ if (!this._resource)
+ return;
+
+ this._responseHeadersRow.dataGrid = this._createNameValueDataGrid(this._resource.responseHeaders);
+ }
+
+ _createNameValueDataGrid(data)
+ {
+ if (!data || data instanceof Array ? !data.length : isEmptyObject(data))
+ return null;
+
+ var dataGrid = new WebInspector.DataGrid({
+ name: {title: WebInspector.UIString("Name"), width: "30%", sortable: true},
+ value: {title: WebInspector.UIString("Value"), sortable: true}
+ });
+ dataGrid.copyTextDelimiter = ": ";
+
+ function addDataGridNode(nodeValue)
+ {
+ console.assert(typeof nodeValue.name === "string");
+ console.assert(!nodeValue.value || typeof nodeValue.value === "string");
+
+ var node = new WebInspector.DataGridNode({name: nodeValue.name, value: nodeValue.value || ""}, false);
+ dataGrid.appendChild(node);
+ }
+
+ if (data instanceof Array) {
+ for (var i = 0; i < data.length; ++i)
+ addDataGridNode(data[i]);
+ } else {
+ for (var name in data)
+ addDataGridNode({name, value: data[name] || ""});
+ }
+
+ dataGrid.addEventListener(WebInspector.DataGrid.Event.SortChanged, sortDataGrid, this);
+
+ function sortDataGrid()
+ {
+ var sortColumnIdentifier = dataGrid.sortColumnIdentifier;
+
+ function comparator(a, b)
+ {
+ var item1 = a.data[sortColumnIdentifier];
+ var item2 = b.data[sortColumnIdentifier];
+ return item1.localeCompare(item2);
+ }
+
+ dataGrid.sortNodes(comparator);
+ }
+
+ return dataGrid;
+ }
+
+ _refreshImageSizeSection()
+ {
+ var resource = this._resource;
+
+ if (!resource)
+ return;
+
+ // Hide the section if we're not dealing with an image or if the load failed.
+ if (resource.type !== WebInspector.Resource.Type.Image || resource.failed) {
+ var imageSectionElement = this._imageSizeSection.element;
+ if (imageSectionElement.parentNode)
+ this.contentView.element.removeChild(imageSectionElement);
+ return;
+ }
+
+ // Ensure the section is displayed, right before the "Location" section.
+ this.contentView.element.insertBefore(this._imageSizeSection.element, this._locationSection.element);
+
+ // Get the metrics for this resource and fill in the metrics rows with that information.
+ resource.getImageSize((size) => {
+ this._imageWidthRow.value = WebInspector.UIString("%dpx").format(size.width);
+ this._imageHeightRow.value = WebInspector.UIString("%dpx").format(size.height);
+ });
+ }
+
+ _goToRequestDataClicked()
+ {
+ WebInspector.showResourceRequest(this._resource);
+ }
+
+ _refreshRequestDataSection()
+ {
+ var resource = this._resource;
+
+ if (!resource)
+ return;
+
+ // Hide the section if we're not dealing with a request with data.
+ var requestData = resource.requestData;
+ if (!requestData) {
+ this._requestDataSection.element.remove();
+ return;
+ }
+
+ // Ensure the section is displayed, right before the "Request Headers" section.
+ this.contentView.element.insertBefore(this._requestDataSection.element, this._requestHeadersSection.element);
+
+ var requestDataContentType = resource.requestDataContentType || "";
+ if (requestDataContentType && requestDataContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i)) {
+ // Simple form data that should be parsable like a query string.
+ var parametersRow = new WebInspector.DetailsSectionDataGridRow(null, WebInspector.UIString("No Parameters"));
+ parametersRow.dataGrid = this._createNameValueDataGrid(parseQueryString(requestData, true));
+
+ this._requestDataSection.groups = [new WebInspector.DetailsSectionGroup([parametersRow])];
+ return;
+ }
+
+ // Not simple form data, so we can really only show the size and type here.
+ // FIXME: Add a go-to arrow here to show the data in the content browser.
+
+ var mimeTypeComponents = parseMIMEType(requestDataContentType);
+
+ var mimeType = mimeTypeComponents.type;
+ var boundary = mimeTypeComponents.boundary;
+ var encoding = mimeTypeComponents.encoding;
+
+ var rows = [];
+
+ var mimeTypeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("MIME Type"));
+ mimeTypeRow.value = mimeType;
+ rows.push(mimeTypeRow);
+
+ if (boundary) {
+ var boundryRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Boundary"));
+ boundryRow.value = boundary;
+ rows.push(boundryRow);
+ }
+
+ if (encoding) {
+ var encodingRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Encoding"));
+ encodingRow.value = encoding;
+ rows.push(encodingRow);
+ }
+
+ var sizeValue = Number.bytesToString(requestData.length);
+
+ var dataValue = document.createDocumentFragment();
+
+ dataValue.append(sizeValue);
+
+ var goToButton = dataValue.appendChild(WebInspector.createGoToArrowButton());
+ goToButton.addEventListener("click", this._goToRequestDataClicked.bind(this));
+
+ var dataRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Data"));
+ dataRow.value = dataValue;
+ rows.push(dataRow);
+
+ this._requestDataSection.groups = [new WebInspector.DetailsSectionGroup(rows)];
+ }
+};