| /* |
| * 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. |
| */ |
| |
| WI.ResourceDetailsSidebarPanel = class ResourceDetailsSidebarPanel extends WI.DetailsSidebarPanel |
| { |
| constructor() |
| { |
| super("resource-details", WI.UIString("Resource")); |
| |
| this.element.classList.add("resource"); |
| |
| this._resource = null; |
| this._needsToApplyResourceEventListeners = false; |
| this._needsToRemoveResourceEventListeners = false; |
| } |
| |
| // 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 WI.Resource to inspect. |
| for (let object of objects) { |
| if (object instanceof WI.Resource) { |
| resourceToInspect = object; |
| break; |
| } |
| |
| if (object instanceof WI.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 WI.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._needsToRemoveResourceEventListeners) { |
| this._resource.removeEventListener(WI.Resource.Event.URLDidChange, this._refreshURL, this); |
| this._resource.removeEventListener(WI.Resource.Event.MIMETypeDidChange, this._refreshMIMEType, this); |
| this._resource.removeEventListener(WI.Resource.Event.TypeDidChange, this._refreshResourceType, this); |
| this._resource.removeEventListener(WI.Resource.Event.LoadingDidFail, this._refreshErrorReason, this); |
| this._resource.removeEventListener(WI.Resource.Event.RequestHeadersDidChange, this._refreshRequestHeaders, this); |
| this._resource.removeEventListener(WI.Resource.Event.ResponseReceived, this._refreshRequestAndResponse, this); |
| this._resource.removeEventListener(WI.Resource.Event.CacheStatusDidChange, this._refreshRequestAndResponse, this); |
| this._resource.removeEventListener(WI.Resource.Event.MetricsDidChange, this._refreshRequestAndResponse, this); |
| this._resource.removeEventListener(WI.Resource.Event.SizeDidChange, this._refreshDecodedSize, this); |
| this._resource.removeEventListener(WI.Resource.Event.TransferSizeDidChange, this._refreshTransferSize, this); |
| this._resource.removeEventListener(WI.Resource.Event.InitiatedResourcesDidChange, this._handleResourceInitiatedResourcesDidChange, this); |
| |
| this._refreshRelatedResourcesSectionThrottler.cancel(); |
| |
| this._needsToRemoveResourceEventListeners = false; |
| } |
| |
| this._resource = resource; |
| |
| if (this._resource) { |
| if (this.didInitialLayout) |
| this._applyResourceEventListeners(); |
| else |
| this._needsToApplyResourceEventListeners = true; |
| } |
| |
| this.needsLayout(); |
| } |
| |
| // Protected |
| |
| initialLayout() |
| { |
| super.initialLayout(); |
| |
| this._typeMIMETypeRow = new WI.DetailsSectionSimpleRow(WI.UIString("MIME Type")); |
| this._typeResourceTypeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Resource Type")); |
| |
| this._typeSection = new WI.DetailsSection("resource-type", WI.UIString("Type")); |
| this._typeSection.groups = [new WI.DetailsSectionGroup([this._typeMIMETypeRow, this._typeResourceTypeRow])]; |
| |
| this._locationFullURLRow = new WI.DetailsSectionSimpleRow(WI.UIString("Full URL")); |
| this._locationSchemeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Scheme")); |
| this._locationHostRow = new WI.DetailsSectionSimpleRow(WI.UIString("Host")); |
| this._locationPortRow = new WI.DetailsSectionSimpleRow(WI.UIString("Port")); |
| this._locationPathRow = new WI.DetailsSectionSimpleRow(WI.UIString("Path")); |
| this._locationQueryStringRow = new WI.DetailsSectionSimpleRow(WI.UIString("Query String")); |
| this._locationFragmentRow = new WI.DetailsSectionSimpleRow(WI.UIString("Fragment")); |
| this._locationFilenameRow = new WI.DetailsSectionSimpleRow(WI.UIString("Filename")); |
| this._initiatorRow = new WI.DetailsSectionSimpleRow(WI.UIString("Initiator")); |
| this._initiatedRow = new WI.DetailsSectionSimpleRow(WI.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 WI.DetailsSectionGroup(firstGroup); |
| this._locationURLComponentsGroup = new WI.DetailsSectionGroup(secondGroup); |
| this._relatedResourcesGroup = new WI.DetailsSectionGroup(thirdGroup); |
| |
| this._locationSection = new WI.DetailsSection("resource-location", WI.UIString("Location"), [this._fullURLGroup, this._locationURLComponentsGroup, this._relatedResourcesGroup]); |
| |
| this._queryParametersRow = new WI.DetailsSectionDataGridRow(null, WI.UIString("No Query Parameters")); |
| this._queryParametersSection = new WI.DetailsSection("resource-query-parameters", WI.UIString("Query Parameters")); |
| this._queryParametersSection.groups = [new WI.DetailsSectionGroup([this._queryParametersRow])]; |
| |
| this._requestDataSection = new WI.DetailsSection("resource-request-data", WI.UIString("Request Data")); |
| |
| this._requestMethodRow = new WI.DetailsSectionSimpleRow(WI.UIString("Method")); |
| this._protocolRow = new WI.DetailsSectionSimpleRow(WI.UIString("Protocol")); |
| this._priorityRow = new WI.DetailsSectionSimpleRow(WI.UIString("Priority")); |
| this._cachedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Cached")); |
| |
| this._statusTextRow = new WI.DetailsSectionSimpleRow(WI.UIString("Status")); |
| this._statusCodeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Code")); |
| this._errorReasonRow = new WI.DetailsSectionSimpleRow(WI.UIString("Error")); |
| |
| this._remoteAddressRow = new WI.DetailsSectionSimpleRow(WI.UIString("IP Address")); |
| this._connectionIdentifierRow = new WI.DetailsSectionSimpleRow(WI.UIString("Connection ID")); |
| |
| this._encodedSizeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Encoded")); |
| this._decodedSizeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Decoded")); |
| this._transferSizeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Transferred")); |
| |
| this._compressedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Compressed")); |
| this._compressionRow = new WI.DetailsSectionSimpleRow(WI.UIString("Compression")); |
| |
| let requestGroup = new WI.DetailsSectionGroup([this._requestMethodRow, this._protocolRow, this._priorityRow, this._cachedRow]); |
| let statusGroup = new WI.DetailsSectionGroup([this._statusTextRow, this._statusCodeRow, this._errorReasonRow]); |
| let connectionGroup = new WI.DetailsSectionGroup([this._remoteAddressRow, this._connectionIdentifierRow]); |
| let sizeGroup = new WI.DetailsSectionGroup([this._encodedSizeRow, this._decodedSizeRow, this._transferSizeRow]); |
| let compressionGroup = new WI.DetailsSectionGroup([this._compressedRow, this._compressionRow]); |
| |
| this._requestAndResponseSection = new WI.DetailsSection("resource-request-response", WI.UIString("Request & Response")); |
| this._requestAndResponseSection.groups = [requestGroup, statusGroup, connectionGroup, sizeGroup, compressionGroup]; |
| |
| this._requestHeadersRow = new WI.DetailsSectionDataGridRow(null, WI.UIString("No Request Headers")); |
| this._requestHeadersSection = new WI.DetailsSection("resource-request-headers", WI.UIString("Request Headers")); |
| this._requestHeadersSection.groups = [new WI.DetailsSectionGroup([this._requestHeadersRow])]; |
| |
| this._responseHeadersRow = new WI.DetailsSectionDataGridRow(null, WI.UIString("No Response Headers")); |
| this._responseHeadersSection = new WI.DetailsSection("resource-response-headers", WI.UIString("Response Headers")); |
| this._responseHeadersSection.groups = [new WI.DetailsSectionGroup([this._responseHeadersRow])]; |
| |
| // Rows for the "Image Size" section. |
| this._imageWidthRow = new WI.DetailsSectionSimpleRow(WI.UIString("Width")); |
| this._imageHeightRow = new WI.DetailsSectionSimpleRow(WI.UIString("Height")); |
| |
| // "Image Size" section where we display intrinsic metrics for image resources. |
| this._imageSizeSection = new WI.DetailsSection("resource-type", WI.UIString("Image Size")); |
| this._imageSizeSection.groups = [new WI.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); |
| |
| if (this._needsToApplyResourceEventListeners) { |
| this._applyResourceEventListeners(); |
| |
| this._needsToApplyResourceEventListeners = false; |
| } |
| } |
| |
| layout() |
| { |
| super.layout(); |
| |
| if (!this._resource) |
| return; |
| |
| this._refreshURL(); |
| this._refreshMIMEType(); |
| this._refreshResourceType(); |
| this._refreshRequestAndResponse(); |
| this._refreshDecodedSize(); |
| this._refreshTransferSize(); |
| this._refreshRequestHeaders(); |
| this._refreshImageSizeSection(); |
| this._refreshRequestDataSection(); |
| this._refreshRelatedResourcesSectionThrottler.force(); |
| } |
| |
| sizeDidChange() |
| { |
| super.sizeDidChange(); |
| |
| // FIXME: <https://webkit.org/b/152269> Web Inspector: Convert DetailsSection classes to use View |
| this._queryParametersRow.sizeDidChange(); |
| this._requestHeadersRow.sizeDidChange(); |
| this._responseHeadersRow.sizeDidChange(); |
| } |
| |
| // Private |
| |
| _refreshURL() |
| { |
| if (!this._resource) |
| return; |
| |
| this._locationFullURLRow.value = this._resource.displayURL.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; |
| if (initiatorLocation) { |
| const options = { |
| dontFloat: true, |
| ignoreSearchTab: true, |
| }; |
| this._initiatorRow.value = WI.createSourceCodeLocationLink(initiatorLocation, options); |
| } else |
| this._initiatorRow.value = null; |
| |
| let initiatedResources = this._resource.initiatedResources; |
| if (initiatedResources.length) { |
| let resourceLinkContainer = document.createElement("div"); |
| for (let resource of initiatedResources) |
| resourceLinkContainer.appendChild(WI.createResourceLink(resource)); |
| |
| this._initiatedRow.value = resourceLinkContainer; |
| } else |
| this._initiatedRow.value = null; |
| } |
| |
| _refreshResourceType() |
| { |
| if (!this._resource) |
| return; |
| |
| this._typeResourceTypeRow.value = WI.Resource.displayNameForType(this._resource.type); |
| } |
| |
| _refreshMIMEType() |
| { |
| if (!this._resource) |
| return; |
| |
| this._typeMIMETypeRow.value = this._resource.mimeType; |
| } |
| |
| _refreshErrorReason() |
| { |
| if (!this._resource) |
| return; |
| |
| if (!this._resource.hadLoadingError()) { |
| this._errorReasonRow.value = null; |
| return; |
| } |
| |
| if (this._resource.failureReasonText) |
| this._errorReasonRow.value = this._resource.failureReasonText; |
| else if (this._resource.statusCode >= 400) |
| this._errorReasonRow.value = WI.UIString("Failure status code"); |
| else if (this._resource.canceled) |
| this._errorReasonRow.value = WI.UIString("Load cancelled"); |
| else |
| this._errorReasonRow.value = WI.UIString("Unknown error"); |
| } |
| |
| _refreshRequestAndResponse() |
| { |
| if (!this._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 = this._resource.requestMethod || emDash; |
| |
| // COMPATIBILITY(iOS 10.3): Network load metrics were not previously available. |
| if (InspectorBackend.hasEvent("Network.loadingFinished", "metrics")) { |
| let protocolDisplayName = WI.Resource.displayNameForProtocol(this._resource.protocol); |
| this._protocolRow.value = protocolDisplayName || emDash; |
| this._protocolRow.tooltip = protocolDisplayName ? this._resource.protocol : ""; |
| this._priorityRow.value = WI.Resource.displayNameForPriority(this._resource.priority) || emDash; |
| this._remoteAddressRow.value = this._resource.remoteAddress || emDash; |
| this._connectionIdentifierRow.value = this._resource.connectionIdentifier || emDash; |
| } |
| |
| this._cachedRow.value = this._cachedRowValue(); |
| |
| this._statusCodeRow.value = this._resource.statusCode || emDash; |
| this._statusTextRow.value = this._resource.statusText || emDash; |
| this._refreshErrorReason(); |
| |
| 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() |
| { |
| if (this._resource.compressed) { |
| this._compressedRow.value = WI.UIString("Yes"); |
| if (!this._resource.size) |
| this._compressionRow.value = emDash; |
| else if (!isNaN(this._resource.networkEncodedSize)) |
| this._compressionRow.value = this._resource.networkEncodedSize ? WI.UIString("%.2f\u00d7").format(this._resource.size / this._resource.networkEncodedSize) : emDash; |
| else |
| this._compressionRow.value = this._resource.estimatedNetworkEncodedSize ? WI.UIString("%.2f\u00d7").format(this._resource.size / this._resource.estimatedNetworkEncodedSize) : emDash; |
| } else { |
| this._compressedRow.value = WI.UIString("No"); |
| this._compressionRow.value = null; |
| } |
| } |
| |
| _refreshDecodedSize() |
| { |
| if (!this._resource) |
| return; |
| |
| let encodedSize = !isNaN(this._resource.networkEncodedSize) ? this._resource.networkEncodedSize : this._resource.estimatedNetworkEncodedSize; |
| let decodedSize = !isNaN(this._resource.networkDecodedSize) ? this._resource.networkDecodedSize : this._resource.size; |
| |
| this._encodedSizeRow.value = this._valueForSize(encodedSize); |
| this._decodedSizeRow.value = this._valueForSize(decodedSize); |
| |
| this._refreshCompressed(); |
| } |
| |
| _refreshTransferSize() |
| { |
| if (!this._resource) |
| return; |
| |
| let encodedSize = !isNaN(this._resource.networkEncodedSize) ? this._resource.networkEncodedSize : this._resource.estimatedNetworkEncodedSize; |
| let transferSize = !isNaN(this._resource.networkTotalTransferSize) ? this._resource.networkTotalTransferSize : this._resource.estimatedTotalTransferSize; |
| |
| this._encodedSizeRow.value = this._valueForSize(encodedSize); |
| this._transferSizeRow.value = this._valueForSize(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 WI.DataGrid({ |
| name: {title: WI.UIString("Name"), width: "30%", sortable: true}, |
| value: {title: WI.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 WI.DataGridNode({name: nodeValue.name, value: nodeValue.value || ""}); |
| 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(WI.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.extendedLocaleCompare(item2); |
| } |
| |
| dataGrid.sortNodes(comparator); |
| } |
| |
| return dataGrid; |
| } |
| |
| _refreshImageSizeSection() |
| { |
| var resource = this._resource; |
| |
| if (!resource) |
| return; |
| |
| function hideImageSection() { |
| this._imageSizeSection.element.remove(); |
| } |
| |
| // Hide the section if we're not dealing with an image or if the load failed. |
| if (resource.type !== WI.Resource.Type.Image || resource.failed) { |
| hideImageSection.call(this); |
| 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) => { |
| if (size) { |
| this._imageWidthRow.value = WI.UIString("%dpx").format(size.width); |
| this._imageHeightRow.value = WI.UIString("%dpx").format(size.height); |
| } else |
| hideImageSection.call(this); |
| }); |
| } |
| |
| _cachedRowValue() |
| { |
| let responseSource = this._resource.responseSource; |
| if (responseSource === WI.Resource.ResponseSource.MemoryCache || responseSource === WI.Resource.ResponseSource.DiskCache) { |
| console.assert(this._resource.cached, "This resource has a cache responseSource it should also be marked as cached", this._resource); |
| let span = document.createElement("span"); |
| let cacheType = document.createElement("span"); |
| cacheType.classList = "cache-type"; |
| cacheType.textContent = responseSource === WI.Resource.ResponseSource.MemoryCache ? WI.UIString("(Memory)") : WI.UIString("(Disk)"); |
| span.append(WI.UIString("Yes"), " ", cacheType); |
| return span; |
| } |
| |
| return this._resource.cached ? WI.UIString("Yes") : WI.UIString("No"); |
| } |
| |
| _goToRequestDataClicked() |
| { |
| const options = { |
| ignoreSearchTab: true, |
| }; |
| WI.showResourceRequest(this._resource, options); |
| } |
| |
| _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 WI.DetailsSectionDataGridRow(null, WI.UIString("No Parameters")); |
| parametersRow.dataGrid = this._createNameValueDataGrid(parseQueryString(requestData, true)); |
| |
| this._requestDataSection.groups = [new WI.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 WI.DetailsSectionSimpleRow(WI.UIString("MIME Type")); |
| mimeTypeRow.value = mimeType; |
| rows.push(mimeTypeRow); |
| |
| if (boundary) { |
| var boundryRow = new WI.DetailsSectionSimpleRow(WI.UIString("Boundary")); |
| boundryRow.value = boundary; |
| rows.push(boundryRow); |
| } |
| |
| if (encoding) { |
| var encodingRow = new WI.DetailsSectionSimpleRow(WI.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(WI.createGoToArrowButton()); |
| goToButton.addEventListener("click", this._goToRequestDataClicked.bind(this)); |
| |
| var dataRow = new WI.DetailsSectionSimpleRow(WI.UIString("Data")); |
| dataRow.value = dataValue; |
| rows.push(dataRow); |
| |
| this._requestDataSection.groups = [new WI.DetailsSectionGroup(rows)]; |
| } |
| |
| _applyResourceEventListeners() |
| { |
| if (!this._refreshRelatedResourcesSectionThrottler) { |
| this._refreshRelatedResourcesSectionThrottler = new Throttler(() => { |
| this._refreshRelatedResourcesSection(); |
| }, 250); |
| } |
| |
| this._resource.addEventListener(WI.Resource.Event.URLDidChange, this._refreshURL, this); |
| this._resource.addEventListener(WI.Resource.Event.MIMETypeDidChange, this._refreshMIMEType, this); |
| this._resource.addEventListener(WI.Resource.Event.TypeDidChange, this._refreshResourceType, this); |
| this._resource.addEventListener(WI.Resource.Event.LoadingDidFail, this._refreshErrorReason, this); |
| this._resource.addEventListener(WI.Resource.Event.RequestHeadersDidChange, this._refreshRequestHeaders, this); |
| this._resource.addEventListener(WI.Resource.Event.ResponseReceived, this._refreshRequestAndResponse, this); |
| this._resource.addEventListener(WI.Resource.Event.CacheStatusDidChange, this._refreshRequestAndResponse, this); |
| this._resource.addEventListener(WI.Resource.Event.MetricsDidChange, this._refreshRequestAndResponse, this); |
| this._resource.addEventListener(WI.Resource.Event.SizeDidChange, this._refreshDecodedSize, this); |
| this._resource.addEventListener(WI.Resource.Event.TransferSizeDidChange, this._refreshTransferSize, this); |
| this._resource.addEventListener(WI.Resource.Event.InitiatedResourcesDidChange, this._handleResourceInitiatedResourcesDidChange, this); |
| |
| this._needsToRemoveResourceEventListeners = true; |
| } |
| |
| _handleResourceInitiatedResourcesDidChange(event) |
| { |
| this._refreshRelatedResourcesSectionThrottler.fire(); |
| } |
| }; |