/* * 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.ResourceCookiesContentView = class ResourceCookiesContentView extends WI.ContentView { constructor(resource) { super(null); console.assert(resource instanceof WI.Resource); this._resource = resource; this._resource.addEventListener(WI.Resource.Event.RequestHeadersDidChange, this._resourceRequestHeadersDidChange, this); this._resource.addEventListener(WI.Resource.Event.ResponseReceived, this._resourceResponseReceived, this); this.element.classList.add("resource-details", "resource-cookies"); } // Table dataSource tableNumberOfRows(table) { return this._dataSourceForTable(table).length; } tableSortChanged(table) { let sortComparator = this._generateSortComparator(table); if (!sortComparator) return; let dataSource = this._dataSourceForTable(table); dataSource.sort(sortComparator); table.reloadData(); } tableIndexForRepresentedObject(table, object) { let cookies = this._dataSourceForTable(table); let index = cookies.indexOf(object); console.assert(index >= 0); return index; } tableRepresentedObjectForIndex(table, index) { let cookies = this._dataSourceForTable(table); console.assert(index >= 0 && index < cookies.length); return cookies[index]; } // Table delegate tableShouldSelectRow(table, cell, column, rowIndex) { return false; } tablePopulateCell(table, cell, column, rowIndex) { let cookie = this._dataSourceForTable(table)[rowIndex]; const checkmark = "\u2713"; switch (column.identifier) { case "name": cell.textContent = cookie.name; break; case "value": cell.textContent = cookie.value; break; case "domain": cell.textContent = cookie.domain || emDash; break; case "path": cell.textContent = cookie.path || emDash; break; case "expires": cell.textContent = (!cookie.session && cookie.expires) ? cookie.expires.toLocaleString() : WI.UIString("Session"); break; case "maxAge": cell.textContent = cookie.maxAge || emDash; break; case "secure": cell.textContent = cookie.secure ? checkmark : zeroWidthSpace; break; case "httpOnly": cell.textContent = cookie.httpOnly ? checkmark : zeroWidthSpace; break; case "sameSite": cell.textContent = cookie.sameSite === WI.Cookie.SameSiteType.None ? emDash : WI.Cookie.displayNameForSameSiteType(cookie.sameSite); break; } return cell; } // Protected initialLayout() { super.initialLayout(); this._requestCookiesSection = new WI.ResourceDetailsSection(WI.UIString("Request Cookies")); this.element.appendChild(this._requestCookiesSection.element); this._refreshRequestCookiesSection(); this._responseCookiesSection = new WI.ResourceDetailsSection(WI.UIString("Response Cookies")); this.element.appendChild(this._responseCookiesSection.element); this._refreshResponseCookiesSection(); } // Private _dataSourceForTable(table) { return table === this._requestCookiesTable ? this._requestCookiesDataSource : this._responseCookiesDataSource; } _generateSortComparator(table) { let sortColumnIdentifier = table.sortColumnIdentifier; if (!sortColumnIdentifier) return null; let comparator; switch (sortColumnIdentifier) { case "name": case "value": case "domain": case "path": case "sameSite": // String. comparator = (a, b) => (a[sortColumnIdentifier] || "").extendedLocaleCompare(b[sortColumnIdentifier] || ""); break; case "maxAge": // Number. comparator = (a, b) => { let aValue = a[sortColumnIdentifier]; if (isNaN(aValue)) return 1; let bValue = b[sortColumnIdentifier]; if (isNaN(bValue)) return -1; return aValue - bValue; }; break; case "httpOnly": case "secure": // Boolean. comparator = (a, b) => a[sortColumnIdentifier] - b[sortColumnIdentifier]; break; case "expires": // Date. comparator = (a, b) => { let aExpires = a.expires; if (!aExpires) return 1; let bExpires = b.expires; if (!bExpires) return -1; return aExpires.getTime() - bExpires.getTime(); }; break; default: console.assert("Unexpected sort column", sortColumnIdentifier); return null; } let reverseFactor = table.sortOrder === WI.Table.SortOrder.Ascending ? 1 : -1; return (a, b) => reverseFactor * comparator(a, b); } _refreshRequestCookiesSection() { let detailsElement = this._requestCookiesSection.detailsElement; detailsElement.removeChildren(); if (this._resource.responseSource === WI.Resource.ResponseSource.MemoryCache) { this._requestCookiesSection.markIncompleteSectionWithMessage(WI.UIString("No request, served from the memory cache.")); return; } this._requestCookiesDataSource = this._resource.requestCookies; if (!this._requestCookiesTable) { this._requestCookiesTable = new WI.Table("request-cookies", this, this, 20); this._requestCookiesTable.addColumn(new WI.TableColumn("name", WI.UIString("Name"), {minWidth: 150, maxWidth: 300, initialWidth: 200, resizeType: WI.TableColumn.ResizeType.Locked})); this._requestCookiesTable.addColumn(new WI.TableColumn("value", WI.UIString("Value"), {minWidth: 150, hideable: false})); if (!this._requestCookiesTable.sortColumnIdentifier) { this._requestCookiesTable.sortOrder = WI.Table.SortOrder.Ascending; this._requestCookiesTable.sortColumnIdentifier = "name"; } } if (!this._requestCookiesDataSource.length) { if (this._requestCookiesTable.isAttached) this.removeSubview(this._requestCookiesTable); this._requestCookiesSection.markIncompleteSectionWithMessage(WI.UIString("No request cookies.")); } else { this._requestCookiesSection.toggleIncomplete(false); this._requestCookiesTable.element.style.height = this._sizeForTable(this._requestCookiesTable) + "px"; this.addSubview(this._requestCookiesTable); detailsElement.classList.add("has-table"); detailsElement.appendChild(this._requestCookiesTable.element); } } _refreshResponseCookiesSection() { let detailsElement = this._responseCookiesSection.detailsElement; detailsElement.removeChildren(); if (!this._resource.hasResponse()) { this._responseCookiesSection.markIncompleteSectionWithLoadingIndicator(); return; } this._responseCookiesDataSource = this._resource.responseCookies; if (!this._responseCookiesTable) { this._responseCookiesTable = new WI.Table("request-cookies", this, this, 20); this._responseCookiesTable.addColumn(new WI.TableColumn("name", WI.UIString("Name"), {minWidth: 150, maxWidth: 300, initialWidth: 200, resizeType: WI.TableColumn.ResizeType.Locked})); this._responseCookiesTable.addColumn(new WI.TableColumn("value", WI.UIString("Value"), {minWidth: 150, hideable: false})); this._responseCookiesTable.addColumn(new WI.TableColumn("domain", WI.unlocalizedString("Domain"), {})); this._responseCookiesTable.addColumn(new WI.TableColumn("path", WI.unlocalizedString("Path"), {})); this._responseCookiesTable.addColumn(new WI.TableColumn("expires", WI.unlocalizedString("Expires"), {maxWidth: 150})); this._responseCookiesTable.addColumn(new WI.TableColumn("maxAge", WI.unlocalizedString("Max-Age"), {maxWidth: 90, align: "right"})); this._responseCookiesTable.addColumn(new WI.TableColumn("secure", WI.unlocalizedString("Secure"), {minWidth: 55, maxWidth: 65, align: "center"})); this._responseCookiesTable.addColumn(new WI.TableColumn("httpOnly", WI.unlocalizedString("HttpOnly"), {minWidth: 55, maxWidth: 65, align: "center"})); this._responseCookiesTable.addColumn(new WI.TableColumn("sameSite", WI.unlocalizedString("SameSite"), {minWidth: 55, maxWidth: 65})); if (!this._responseCookiesTable.sortColumnIdentifier) { this._responseCookiesTable.sortOrder = WI.Table.SortOrder.Ascending; this._responseCookiesTable.sortColumnIdentifier = "name"; } } if (!this._responseCookiesDataSource.length) { if (this._responseCookiesTable.isAttached) this.removeSubview(this._responseCookiesTable); this._responseCookiesSection.markIncompleteSectionWithMessage(WI.UIString("No response cookies.")); } else { this._responseCookiesSection.toggleIncomplete(false); this._responseCookiesTable.element.style.height = this._sizeForTable(this._responseCookiesTable) + "px"; this.addSubview(this._responseCookiesTable); detailsElement.classList.add("has-table"); detailsElement.appendChild(this._responseCookiesTable.element); } } _sizeForTable(table) { const headerHeight = 28; const borderHeight = 3; let rowsHeight = this._dataSourceForTable(table).length * table.rowHeight; return rowsHeight + headerHeight + borderHeight; } _resourceRequestHeadersDidChange(event) { this._refreshRequestCookiesSection(); } _resourceResponseReceived(event) { this._refreshResponseCookiesSection(); } }; WI.ResourceCookiesContentView.ReferencePage = WI.ReferencePage.NetworkTab.CookiesPane;