|
Line 0
a/Source/WebInspectorUI/UserInterface/Views/CookiePopover.js_sec1
|
|
|
1 |
/* |
| 2 |
* Copyright (C) 2020 Apple Inc. All rights reserved. |
| 3 |
* |
| 4 |
* Redistribution and use in source and binary forms, with or without |
| 5 |
* modification, are permitted provided that the following conditions |
| 6 |
* are met: |
| 7 |
* 1. Redistributions of source code must retain the above copyright |
| 8 |
* notice, this list of conditions and the following disclaimer. |
| 9 |
* 2. Redistributions in binary form must reproduce the above copyright |
| 10 |
* notice, this list of conditions and the following disclaimer in the |
| 11 |
* documentation and/or other materials provided with the distribution. |
| 12 |
* |
| 13 |
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| 14 |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| 15 |
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| 17 |
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 18 |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 19 |
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 20 |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 21 |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 22 |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| 23 |
* THE POSSIBILITY OF SUCH DAMAGE. |
| 24 |
*/ |
| 25 |
|
| 26 |
WI.CookiePopover = class CookiePopover extends WI.Popover |
| 27 |
{ |
| 28 |
constructor(delegate) |
| 29 |
{ |
| 30 |
super(delegate); |
| 31 |
|
| 32 |
this._nameInputElement = null; |
| 33 |
this._valueInputElement = null; |
| 34 |
this._domainInputElement = null; |
| 35 |
this._pathInputElement = null; |
| 36 |
this._sessionCheckboxElement = null; |
| 37 |
this._expiresInputElement = null; |
| 38 |
this._httpOnlyCheckboxElement = null; |
| 39 |
this._secureCheckboxElement = null; |
| 40 |
this._sameSiteSelectElement = null; |
| 41 |
|
| 42 |
this._serializedDataWhenShown = null; |
| 43 |
|
| 44 |
this.windowResizeHandler = this._presentOverTargetElement.bind(this); |
| 45 |
} |
| 46 |
|
| 47 |
// Public |
| 48 |
|
| 49 |
get serializedData() |
| 50 |
{ |
| 51 |
if (!this._targetElement) |
| 52 |
return null; |
| 53 |
|
| 54 |
let name = this._nameInputElement.value || this._nameInputElement.placeholder; |
| 55 |
if (!name) |
| 56 |
return null; |
| 57 |
|
| 58 |
let value = this._valueInputElement.value || this._valueInputElement.placeholder; |
| 59 |
if (!value) |
| 60 |
return null; |
| 61 |
|
| 62 |
let domain = this._domainInputElement.value || this._domainInputElement.placeholder; |
| 63 |
if (!domain) |
| 64 |
return null; |
| 65 |
|
| 66 |
let path = this._pathInputElement.value || this._pathInputElement.placeholder; |
| 67 |
if (!path) |
| 68 |
return null; |
| 69 |
|
| 70 |
let session = this._sessionCheckboxElement.checked; |
| 71 |
let expires = this._parseExpires(); |
| 72 |
if (!session && isNaN(expires)) |
| 73 |
return null; |
| 74 |
|
| 75 |
// If a full URL is entered in the domain input, parse it to get just the domain. |
| 76 |
try { |
| 77 |
let url = new URL(domain); |
| 78 |
domain = url.hostname; |
| 79 |
} catch { } |
| 80 |
|
| 81 |
if (!path.startsWith("/")) |
| 82 |
path = "/" + path; |
| 83 |
|
| 84 |
let data = { |
| 85 |
name, |
| 86 |
value, |
| 87 |
domain, |
| 88 |
path, |
| 89 |
httpOnly: this._httpOnlyCheckboxElement.checked, |
| 90 |
secure: this._secureCheckboxElement.checked, |
| 91 |
sameSite: this._sameSiteSelectElement.value, |
| 92 |
}; |
| 93 |
|
| 94 |
if (session) |
| 95 |
data.session = true; |
| 96 |
else |
| 97 |
data.expires = expires; |
| 98 |
|
| 99 |
if (JSON.stringify(data) === JSON.stringify(this._serializedDataWhenShown)) |
| 100 |
return null; |
| 101 |
|
| 102 |
return data; |
| 103 |
} |
| 104 |
|
| 105 |
show(cookie, targetElement, preferredEdges) |
| 106 |
{ |
| 107 |
console.assert(!cookie || cookie instanceof WI.Cookie, cookie); |
| 108 |
console.assert(targetElement instanceof Element, targetElement); |
| 109 |
console.assert(Array.isArray(preferredEdges), preferredEdges); |
| 110 |
|
| 111 |
this._targetElement = targetElement; |
| 112 |
this._preferredEdges = preferredEdges; |
| 113 |
|
| 114 |
let data = {}; |
| 115 |
if (cookie) { |
| 116 |
data.name = cookie.name; |
| 117 |
data.value = cookie.value; |
| 118 |
data.domain = cookie.domain; |
| 119 |
data.path = cookie.path; |
| 120 |
data.expires = (cookie.expires || this._defaultExpires()).toLocaleString(); |
| 121 |
data.session = cookie.session; |
| 122 |
data.httpOnly = cookie.httpOnly; |
| 123 |
data.secure = cookie.secure; |
| 124 |
data.sameSite = cookie.sameSite; |
| 125 |
} else { |
| 126 |
let urlComponents = WI.networkManager.mainFrame.mainResource.urlComponents; |
| 127 |
data.name = WI.unlocalizedString("name"); |
| 128 |
data.value = WI.unlocalizedString("value"); |
| 129 |
data.domain = urlComponents.host; |
| 130 |
data.path = urlComponents.path; |
| 131 |
data.expires = this._defaultExpires().toLocaleString(); |
| 132 |
data.session = true; |
| 133 |
data.httpOnly = false; |
| 134 |
data.secure = false; |
| 135 |
data.sameSite = WI.Cookie.SameSiteType.None; |
| 136 |
} |
| 137 |
|
| 138 |
let popoverContentElement = document.createElement("div"); |
| 139 |
popoverContentElement.className = "cookie-popover-content"; |
| 140 |
|
| 141 |
let tableElement = popoverContentElement.appendChild(document.createElement("table")); |
| 142 |
|
| 143 |
function createRow(id, label, editorElement) { |
| 144 |
id = `cookie-popover-${id}-editor`; |
| 145 |
|
| 146 |
let rowElement = tableElement.appendChild(document.createElement("tr")); |
| 147 |
|
| 148 |
let headerElement = rowElement.appendChild(document.createElement("th")); |
| 149 |
|
| 150 |
let labelElement = headerElement.appendChild(document.createElement("label")); |
| 151 |
labelElement.setAttribute("for", id); |
| 152 |
labelElement.textContent = label; |
| 153 |
|
| 154 |
let dataElement = rowElement.appendChild(document.createElement("td")); |
| 155 |
|
| 156 |
editorElement.id = id; |
| 157 |
dataElement.appendChild(editorElement); |
| 158 |
|
| 159 |
return {rowElement}; |
| 160 |
} |
| 161 |
|
| 162 |
let boundHandleInputKeyDown = this._handleInputKeyDown.bind(this); |
| 163 |
|
| 164 |
function createInputRow(id, label, type, value) { |
| 165 |
let inputElement = document.createElement("input"); |
| 166 |
inputElement.type = type; |
| 167 |
|
| 168 |
if (type === "checkbox") |
| 169 |
inputElement.checked = value; |
| 170 |
else { |
| 171 |
if (cookie) |
| 172 |
inputElement.value = value; |
| 173 |
inputElement.placeholder = value; |
| 174 |
inputElement.addEventListener("keydown", boundHandleInputKeyDown); |
| 175 |
} |
| 176 |
|
| 177 |
let rowElement = createRow(id, label, inputElement).rowElement; |
| 178 |
|
| 179 |
return {inputElement, rowElement}; |
| 180 |
} |
| 181 |
|
| 182 |
this._nameInputElement = createInputRow("name", WI.UIString("Name"), "text", data.name).inputElement; |
| 183 |
|
| 184 |
this._valueInputElement = createInputRow("value", WI.UIString("Value"), "text", data.value).inputElement; |
| 185 |
|
| 186 |
this._domainInputElement = createInputRow("domain", WI.unlocalizedString("Domain"), "text", data.domain).inputElement; |
| 187 |
|
| 188 |
this._pathInputElement = createInputRow("path", WI.unlocalizedString("Path"), "text", data.path).inputElement; |
| 189 |
|
| 190 |
this._sessionCheckboxElement = createInputRow("session", WI.unlocalizedString("Session"), "checkbox", data.session).inputElement; |
| 191 |
|
| 192 |
let expiresInputRow = createInputRow("expires", WI.unlocalizedString("Expires"), "datetime-local", data.expires); |
| 193 |
this._expiresInputElement = expiresInputRow.inputElement; |
| 194 |
this._expiresInputElement.addEventListener("input", (event) => { |
| 195 |
this._expiresInputElement.classList.toggle("invalid", isNaN(this._parseExpires())); |
| 196 |
}); |
| 197 |
|
| 198 |
this._httpOnlyCheckboxElement = createInputRow("http-only", WI.unlocalizedString("HttpOnly"), "checkbox", data.httpOnly).inputElement; |
| 199 |
|
| 200 |
this._secureCheckboxElement = createInputRow("secure", WI.unlocalizedString("Secure"), "checkbox", data.secure).inputElement; |
| 201 |
|
| 202 |
this._sameSiteSelectElement = document.createElement("select"); |
| 203 |
for (let sameSiteType of Object.values(WI.Cookie.SameSiteType)) { |
| 204 |
let optionElement = this._sameSiteSelectElement.appendChild(document.createElement("option")); |
| 205 |
optionElement.textContent = sameSiteType; |
| 206 |
} |
| 207 |
createRow("same-site", WI.unlocalizedString("SameSite"), this._sameSiteSelectElement); |
| 208 |
|
| 209 |
let toggleExpiresRow = () => { |
| 210 |
expiresInputRow.rowElement.hidden = this._sessionCheckboxElement.checked; |
| 211 |
|
| 212 |
this.update(); |
| 213 |
}; |
| 214 |
|
| 215 |
this._sessionCheckboxElement.addEventListener("change", (event) => { |
| 216 |
toggleExpiresRow(); |
| 217 |
}); |
| 218 |
|
| 219 |
toggleExpiresRow(); |
| 220 |
|
| 221 |
this._serializedDataWhenShown = this.serializedData; |
| 222 |
|
| 223 |
this.content = popoverContentElement; |
| 224 |
this._presentOverTargetElement(); |
| 225 |
} |
| 226 |
|
| 227 |
// Private |
| 228 |
|
| 229 |
_presentOverTargetElement() |
| 230 |
{ |
| 231 |
if (!this._targetElement) |
| 232 |
return; |
| 233 |
|
| 234 |
let targetFrame = WI.Rect.rectFromClientRect(this._targetElement.getBoundingClientRect()); |
| 235 |
this.present(targetFrame.pad(2), this._preferredEdges); |
| 236 |
} |
| 237 |
|
| 238 |
_defaultExpires() |
| 239 |
{ |
| 240 |
return new Date(Date.now() + (1000 * 60 * 60 * 24)); // one day in the future |
| 241 |
} |
| 242 |
|
| 243 |
_parseExpires() |
| 244 |
{ |
| 245 |
let timestamp = Date.parse(this._expiresInputElement.value || this._expiresInputElement.placeholder); |
| 246 |
if (timestamp < Date.now()) |
| 247 |
return NaN; |
| 248 |
return timestamp; |
| 249 |
} |
| 250 |
|
| 251 |
_handleInputKeyDown(event) |
| 252 |
{ |
| 253 |
if (event.key === "Enter" || event.key === "Esc") |
| 254 |
this.dismiss(); |
| 255 |
} |
| 256 |
}; |