11cb0ef41Sopenharmony_ci// Copyright 2021 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ciimport {kChunkVisualWidth, MapLogEntry} from '../../log/map.mjs'; 61cb0ef41Sopenharmony_ciimport {FocusEvent} from '../events.mjs'; 71cb0ef41Sopenharmony_ciimport {CSSColor, DOM} from '../helper.mjs'; 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ciimport {TimelineTrackBase} from './timeline-track-base.mjs' 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ciDOM.defineCustomElement('view/timeline/timeline-track', 'timeline-track-map', 121cb0ef41Sopenharmony_ci (templateText) => 131cb0ef41Sopenharmony_ci class TimelineTrackMap extends TimelineTrackBase { 141cb0ef41Sopenharmony_ci constructor() { 151cb0ef41Sopenharmony_ci super(templateText); 161cb0ef41Sopenharmony_ci this.navigation = new Navigation(this) 171cb0ef41Sopenharmony_ci } 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_ci _handleKeyDown(event) {} 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci getMapStyle(map) { 221cb0ef41Sopenharmony_ci return map.edge && map.edge.from ? CSSColor.onBackgroundColor : 231cb0ef41Sopenharmony_ci CSSColor.onPrimaryColor; 241cb0ef41Sopenharmony_ci } 251cb0ef41Sopenharmony_ci 261cb0ef41Sopenharmony_ci markMap(map) { 271cb0ef41Sopenharmony_ci const [x, y] = map.position(this.chunks); 281cb0ef41Sopenharmony_ci const strokeColor = this.getMapStyle(map); 291cb0ef41Sopenharmony_ci return `<circle cx=${x} cy=${y} r=${2} stroke=${ 301cb0ef41Sopenharmony_ci strokeColor} class=annotationPoint />` 311cb0ef41Sopenharmony_ci } 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ci markSelectedMap(map) { 341cb0ef41Sopenharmony_ci const [x, y] = map.position(this.chunks); 351cb0ef41Sopenharmony_ci const strokeColor = this.getMapStyle(map); 361cb0ef41Sopenharmony_ci return `<circle cx=${x} cy=${y} r=${3} stroke=${ 371cb0ef41Sopenharmony_ci strokeColor} class=annotationPoint />` 381cb0ef41Sopenharmony_ci } 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ci _drawAnnotations(logEntry, time) { 411cb0ef41Sopenharmony_ci if (!(logEntry instanceof MapLogEntry)) return; 421cb0ef41Sopenharmony_ci if (!logEntry.edge) { 431cb0ef41Sopenharmony_ci this.timelineAnnotationsNode.innerHTML = ''; 441cb0ef41Sopenharmony_ci return; 451cb0ef41Sopenharmony_ci } 461cb0ef41Sopenharmony_ci // Draw the trace of maps in reverse order to make sure the outgoing 471cb0ef41Sopenharmony_ci // transitions of previous maps aren't drawn over. 481cb0ef41Sopenharmony_ci const kOpaque = 1.0; 491cb0ef41Sopenharmony_ci let stack = []; 501cb0ef41Sopenharmony_ci let current = logEntry; 511cb0ef41Sopenharmony_ci while (current !== undefined) { 521cb0ef41Sopenharmony_ci stack.push(current); 531cb0ef41Sopenharmony_ci current = current.parent; 541cb0ef41Sopenharmony_ci } 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ci // Draw outgoing refs as fuzzy background. Skip the last map entry. 571cb0ef41Sopenharmony_ci let buffer = ''; 581cb0ef41Sopenharmony_ci let nofEdges = 0; 591cb0ef41Sopenharmony_ci const kMaxOutgoingEdges = 100; 601cb0ef41Sopenharmony_ci for (let i = stack.length - 2; i >= 0; i--) { 611cb0ef41Sopenharmony_ci const map = stack[i].parent; 621cb0ef41Sopenharmony_ci nofEdges += map.children.length; 631cb0ef41Sopenharmony_ci if (nofEdges > kMaxOutgoingEdges) break; 641cb0ef41Sopenharmony_ci buffer += this.drawOutgoingEdges(map, 0.4, 1); 651cb0ef41Sopenharmony_ci } 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_ci // Draw main connection. 681cb0ef41Sopenharmony_ci let labelOffset = 15; 691cb0ef41Sopenharmony_ci let xPrev = 0; 701cb0ef41Sopenharmony_ci for (let i = stack.length - 1; i >= 0; i--) { 711cb0ef41Sopenharmony_ci let map = stack[i]; 721cb0ef41Sopenharmony_ci if (map.edge) { 731cb0ef41Sopenharmony_ci const [xTo, data] = this.drawEdge(map.edge, kOpaque, labelOffset); 741cb0ef41Sopenharmony_ci buffer += data; 751cb0ef41Sopenharmony_ci if (xTo == xPrev) { 761cb0ef41Sopenharmony_ci labelOffset += 10; 771cb0ef41Sopenharmony_ci } else { 781cb0ef41Sopenharmony_ci labelOffset = 15 791cb0ef41Sopenharmony_ci } 801cb0ef41Sopenharmony_ci xPrev = xTo; 811cb0ef41Sopenharmony_ci } 821cb0ef41Sopenharmony_ci buffer += this.markMap(map); 831cb0ef41Sopenharmony_ci } 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci buffer += this.drawOutgoingEdges(logEntry, 0.9, 3); 861cb0ef41Sopenharmony_ci // Mark selected map 871cb0ef41Sopenharmony_ci buffer += this.markSelectedMap(logEntry); 881cb0ef41Sopenharmony_ci this.timelineAnnotationsNode.innerHTML = buffer; 891cb0ef41Sopenharmony_ci } 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ci drawEdge(edge, opacity, labelOffset = 20) { 921cb0ef41Sopenharmony_ci let buffer = ''; 931cb0ef41Sopenharmony_ci if (!edge.from || !edge.to) return [-1, buffer]; 941cb0ef41Sopenharmony_ci const [xFrom, yFrom] = edge.from.position(this.chunks); 951cb0ef41Sopenharmony_ci const [xTo, yTo] = edge.to.position(this.chunks); 961cb0ef41Sopenharmony_ci const sameChunk = xTo == xFrom; 971cb0ef41Sopenharmony_ci if (sameChunk) labelOffset += 10; 981cb0ef41Sopenharmony_ci const color = this._legend.colorForType(edge.type); 991cb0ef41Sopenharmony_ci const offsetX = 20; 1001cb0ef41Sopenharmony_ci const midX = xFrom + (xTo - xFrom) / 2; 1011cb0ef41Sopenharmony_ci const midY = (yFrom + yTo) / 2 - 100; 1021cb0ef41Sopenharmony_ci if (!sameChunk) { 1031cb0ef41Sopenharmony_ci if (opacity == 1.0) { 1041cb0ef41Sopenharmony_ci buffer += `<path d="M ${xFrom} ${yFrom} Q ${midX} ${midY}, ${xTo} ${ 1051cb0ef41Sopenharmony_ci yTo}" class=strokeBG />` 1061cb0ef41Sopenharmony_ci } 1071cb0ef41Sopenharmony_ci buffer += `<path d="M ${xFrom} ${yFrom} Q ${midX} ${midY}, ${xTo} ${ 1081cb0ef41Sopenharmony_ci yTo}" stroke=${color} fill=none opacity=${opacity} />` 1091cb0ef41Sopenharmony_ci } else { 1101cb0ef41Sopenharmony_ci if (opacity == 1.0) { 1111cb0ef41Sopenharmony_ci buffer += `<line x1=${xFrom} x2=${xTo} y1=${yFrom} y2=${ 1121cb0ef41Sopenharmony_ci yTo} class=strokeBG />`; 1131cb0ef41Sopenharmony_ci } 1141cb0ef41Sopenharmony_ci buffer += `<line x1=${xFrom} x2=${xTo} y1=${yFrom} y2=${yTo} stroke=${ 1151cb0ef41Sopenharmony_ci color} fill=none opacity=${opacity} />`; 1161cb0ef41Sopenharmony_ci } 1171cb0ef41Sopenharmony_ci if (opacity == 1.0) { 1181cb0ef41Sopenharmony_ci const centerX = sameChunk ? xTo : ((xFrom / 2 + midX + xTo / 2) / 2) | 0; 1191cb0ef41Sopenharmony_ci const centerY = sameChunk ? yTo : ((yFrom / 2 + midY + yTo / 2) / 2) | 0; 1201cb0ef41Sopenharmony_ci const centerYTo = centerY - labelOffset; 1211cb0ef41Sopenharmony_ci buffer += `<line x1=${centerX} x2=${centerX + offsetX} y1=${centerY} y2=${ 1221cb0ef41Sopenharmony_ci centerYTo} stroke=${color} fill=none opacity=${opacity} />`; 1231cb0ef41Sopenharmony_ci buffer += `<text x=${centerX + offsetX + 2} y=${ 1241cb0ef41Sopenharmony_ci centerYTo} class=annotationLabel opacity=${opacity} >${ 1251cb0ef41Sopenharmony_ci edge.toString()}</text>`; 1261cb0ef41Sopenharmony_ci } 1271cb0ef41Sopenharmony_ci return [xTo, buffer]; 1281cb0ef41Sopenharmony_ci } 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_ci drawOutgoingEdges(map, opacity = 1.0, max = 10, depth = 0) { 1311cb0ef41Sopenharmony_ci let buffer = ''; 1321cb0ef41Sopenharmony_ci if (!map || depth >= max) return buffer; 1331cb0ef41Sopenharmony_ci const limit = Math.min(map.children.length, 100) 1341cb0ef41Sopenharmony_ci for (let i = 0; i < limit; i++) { 1351cb0ef41Sopenharmony_ci const edge = map.children[i]; 1361cb0ef41Sopenharmony_ci const [xTo, data] = this.drawEdge(edge, opacity); 1371cb0ef41Sopenharmony_ci buffer += data; 1381cb0ef41Sopenharmony_ci buffer += this.drawOutgoingEdges(edge.to, opacity * 0.5, max, depth + 1); 1391cb0ef41Sopenharmony_ci } 1401cb0ef41Sopenharmony_ci return buffer; 1411cb0ef41Sopenharmony_ci } 1421cb0ef41Sopenharmony_ci}) 1431cb0ef41Sopenharmony_ci 1441cb0ef41Sopenharmony_ciclass Navigation { 1451cb0ef41Sopenharmony_ci constructor(track) { 1461cb0ef41Sopenharmony_ci this._track = track; 1471cb0ef41Sopenharmony_ci this._track.addEventListener('keydown', this._handleKeyDown.bind(this)); 1481cb0ef41Sopenharmony_ci this._map = undefined; 1491cb0ef41Sopenharmony_ci } 1501cb0ef41Sopenharmony_ci 1511cb0ef41Sopenharmony_ci _handleKeyDown(event) { 1521cb0ef41Sopenharmony_ci if (!this._track.isFocused) return; 1531cb0ef41Sopenharmony_ci let handled = false; 1541cb0ef41Sopenharmony_ci switch (event.key) { 1551cb0ef41Sopenharmony_ci case 'ArrowDown': 1561cb0ef41Sopenharmony_ci handled = true; 1571cb0ef41Sopenharmony_ci if (event.shiftKey) { 1581cb0ef41Sopenharmony_ci this.selectPrevEdge(); 1591cb0ef41Sopenharmony_ci } else { 1601cb0ef41Sopenharmony_ci this.moveInChunk(-1); 1611cb0ef41Sopenharmony_ci } 1621cb0ef41Sopenharmony_ci break; 1631cb0ef41Sopenharmony_ci case 'ArrowUp': 1641cb0ef41Sopenharmony_ci handled = true; 1651cb0ef41Sopenharmony_ci if (event.shiftKey) { 1661cb0ef41Sopenharmony_ci this.selectNextEdge(); 1671cb0ef41Sopenharmony_ci } else { 1681cb0ef41Sopenharmony_ci this.moveInChunk(1); 1691cb0ef41Sopenharmony_ci } 1701cb0ef41Sopenharmony_ci break; 1711cb0ef41Sopenharmony_ci case 'ArrowLeft': 1721cb0ef41Sopenharmony_ci handled = true; 1731cb0ef41Sopenharmony_ci this.moveInChunks(false); 1741cb0ef41Sopenharmony_ci break; 1751cb0ef41Sopenharmony_ci case 'ArrowRight': 1761cb0ef41Sopenharmony_ci handled = true; 1771cb0ef41Sopenharmony_ci this.moveInChunks(true); 1781cb0ef41Sopenharmony_ci break; 1791cb0ef41Sopenharmony_ci case 'Enter': 1801cb0ef41Sopenharmony_ci handled = true; 1811cb0ef41Sopenharmony_ci this.selectMap(); 1821cb0ef41Sopenharmony_ci break 1831cb0ef41Sopenharmony_ci } 1841cb0ef41Sopenharmony_ci if (handled) { 1851cb0ef41Sopenharmony_ci event.stopPropagation(); 1861cb0ef41Sopenharmony_ci event.preventDefault(); 1871cb0ef41Sopenharmony_ci return false; 1881cb0ef41Sopenharmony_ci } 1891cb0ef41Sopenharmony_ci } 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci get map() { 1921cb0ef41Sopenharmony_ci return this._track.focusedEntry; 1931cb0ef41Sopenharmony_ci } 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ci set map(map) { 1961cb0ef41Sopenharmony_ci this._track.focusedEntry = map; 1971cb0ef41Sopenharmony_ci } 1981cb0ef41Sopenharmony_ci 1991cb0ef41Sopenharmony_ci get chunks() { 2001cb0ef41Sopenharmony_ci return this._track.chunks; 2011cb0ef41Sopenharmony_ci } 2021cb0ef41Sopenharmony_ci 2031cb0ef41Sopenharmony_ci selectMap() { 2041cb0ef41Sopenharmony_ci if (!this.map) return; 2051cb0ef41Sopenharmony_ci this._track.dispatchEvent(new FocusEvent(this.map)) 2061cb0ef41Sopenharmony_ci } 2071cb0ef41Sopenharmony_ci 2081cb0ef41Sopenharmony_ci selectNextEdge() { 2091cb0ef41Sopenharmony_ci if (!this.map) return; 2101cb0ef41Sopenharmony_ci if (this.map.children.length != 1) return; 2111cb0ef41Sopenharmony_ci this.show(this.map.children[0].to); 2121cb0ef41Sopenharmony_ci } 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_ci selectPrevEdge() { 2151cb0ef41Sopenharmony_ci if (!this.map) return; 2161cb0ef41Sopenharmony_ci if (!this.map.parent) return; 2171cb0ef41Sopenharmony_ci this.map = this.map.parent; 2181cb0ef41Sopenharmony_ci } 2191cb0ef41Sopenharmony_ci 2201cb0ef41Sopenharmony_ci selectDefaultMap() { 2211cb0ef41Sopenharmony_ci this.map = this.chunks[0].at(0); 2221cb0ef41Sopenharmony_ci } 2231cb0ef41Sopenharmony_ci 2241cb0ef41Sopenharmony_ci moveInChunks(next) { 2251cb0ef41Sopenharmony_ci if (!this.map) return this.selectDefaultMap(); 2261cb0ef41Sopenharmony_ci let chunkIndex = this.map.chunkIndex(this.chunks); 2271cb0ef41Sopenharmony_ci let currentChunk = this.chunks[chunkIndex]; 2281cb0ef41Sopenharmony_ci let currentIndex = currentChunk.indexOf(this.map); 2291cb0ef41Sopenharmony_ci let newChunk; 2301cb0ef41Sopenharmony_ci if (next) { 2311cb0ef41Sopenharmony_ci newChunk = chunk.next(this.chunks); 2321cb0ef41Sopenharmony_ci } else { 2331cb0ef41Sopenharmony_ci newChunk = chunk.prev(this.chunks); 2341cb0ef41Sopenharmony_ci } 2351cb0ef41Sopenharmony_ci if (!newChunk) return; 2361cb0ef41Sopenharmony_ci let newIndex = Math.min(currentIndex, newChunk.size() - 1); 2371cb0ef41Sopenharmony_ci this.map = newChunk.at(newIndex); 2381cb0ef41Sopenharmony_ci } 2391cb0ef41Sopenharmony_ci 2401cb0ef41Sopenharmony_ci moveInChunk(delta) { 2411cb0ef41Sopenharmony_ci if (!this.map) return this.selectDefaultMap(); 2421cb0ef41Sopenharmony_ci let chunkIndex = this.map.chunkIndex(this.chunks) 2431cb0ef41Sopenharmony_ci let chunk = this.chunks[chunkIndex]; 2441cb0ef41Sopenharmony_ci let index = chunk.indexOf(this.map) + delta; 2451cb0ef41Sopenharmony_ci let map; 2461cb0ef41Sopenharmony_ci if (index < 0) { 2471cb0ef41Sopenharmony_ci map = chunk.prev(this.chunks).last(); 2481cb0ef41Sopenharmony_ci } else if (index >= chunk.size()) { 2491cb0ef41Sopenharmony_ci map = chunk.next(this.chunks).first() 2501cb0ef41Sopenharmony_ci } else { 2511cb0ef41Sopenharmony_ci map = chunk.at(index); 2521cb0ef41Sopenharmony_ci } 2531cb0ef41Sopenharmony_ci this.map = map; 2541cb0ef41Sopenharmony_ci } 2551cb0ef41Sopenharmony_ci} 256