[tools][system-analyzer] Unify CustomEvents

This CL unifies the custom events by creating
classes specialised based on the event type.
Multiple entry selection causes panels to
emit 'showentries' event. Single entry selection
causes panels to emit 'showentrydetail' event.
The events are received by the controller App class
and updates the view of the panels and state of the app.

Bug: v8:10644

Change-Id: Ibe26223459ba605c6d6d3f0025bf3a556dfb0578
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2335188
Commit-Queue: Zeynep Cankara <zcankara@google.com>
Reviewed-by: Sathya Gunasekaran  <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69286}
This commit is contained in:
Zeynep Cankara 2020-08-06 15:41:38 +01:00 committed by Commit Bot
parent b400c9b705
commit 393e434479
9 changed files with 80 additions and 95 deletions

View File

@ -0,0 +1,23 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
class SelectionEvent extends CustomEvent {
constructor(entries){
super('showentries', {bubbles: true, composed: true});
if(!Array.isArray(entries) || entries.length == 0){
throw new Error('No valid entries selected!')
}
this.entries = entries;
}
}
class SelectEvent extends CustomEvent {
constructor(entry){
super('showentrydetail', {bubbles: true, composed: true});
this.entry = entry;
}
}
export {SelectionEvent, SelectEvent};

View File

@ -4,6 +4,7 @@
import {Group} from './ic-model.mjs';
import CustomIcProcessor from "./ic-processor.mjs";
import {SelectEvent} from './events.mjs';
import {defineCustomElement, V8CustomElement} from './helper.mjs';
defineCustomElement('ic-panel', (templateText) =>
@ -92,14 +93,11 @@ defineCustomElement('ic-panel', (templateText) =>
}
handleMapClick(e){
this.dispatchEvent(new CustomEvent(
'mapclick', {bubbles: true, composed: true,
detail: e.target.parentNode.entry}));
this.dispatchEvent(new SelectEvent(e.target.parentNode.entry));
}
handleFilePositionClick(e){
this.dispatchEvent(new CustomEvent(
'filepositionclick', {bubbles: true, composed: true,
detail: e.target.parentNode.entry}));
this.dispatchEvent(new SelectEvent(e.target.parentNode.entry.key));
}
render(entries, parent) {

View File

@ -208,7 +208,7 @@ class CustomIcProcessor extends IcProcessor {
return this.timeline;
}
};
// TODO(zcankara) Change name to Entry -> V8IcEntry or V8Ic
class Entry extends Event {
constructor(
type, fn_file, time, line, column, key, oldState, newState, map, reason,
@ -262,4 +262,4 @@ class Entry extends Event {
}
}
export { CustomIcProcessor as default };
export { CustomIcProcessor as default, Entry};

View File

@ -3,9 +3,9 @@
// found in the LICENSE file.
import CustomIcProcessor from "./ic-processor.mjs";
import {Entry} from "./ic-processor.mjs";
import {State} from './app-model.mjs';
import {MapProcessor, V8Map} from './map-processor.mjs';
import {Chunk} from './timeline.mjs';
import {$} from './helper.mjs';
import './ic-panel.mjs';
import './timeline-panel.mjs';
@ -25,64 +25,54 @@ class App {
icTrack: $(icTrackId),
}
this.#state = new State();
this.toggleSwitch = $('.theme-switch input[type="checkbox"]');
this.toggleSwitch.addEventListener('change', e => this.switchTheme(e));
this.#view.logFileReader.addEventListener('fileuploadstart',
e => this.handleFileUpload(e));
this.#view.logFileReader.addEventListener('fileuploadend',
e => this.handleDataUpload(e));
this.toggleSwitch = $('.theme-switch input[type="checkbox"]');
this.toggleSwitch.addEventListener('change', e => this.switchTheme(e));
this.#view.timelinePanel.addEventListener(
'mapchange', e => this.handleMapChange(e));
this.#view.timelinePanel.addEventListener(
'showmaps', e => this.handleShowMaps(e));
this.#view.mapPanel.addEventListener(
'mapchange', e => this.handleMapChange(e));
this.#view.mapPanel.addEventListener(
'selectmapdblclick', e => this.handleDblClickSelectMap(e));
this.#view.mapPanel.addEventListener(
'sourcepositionsclick', e => this.handleClickSourcePositions(e));
Object.entries(this.#view).forEach(([_, value]) => {
value.addEventListener('showentries',
e => this.handleShowEntries(e));
value.addEventListener('showentrydetail',
e => this.handleShowEntryDetail(e));
});
this.#view.icPanel.addEventListener(
'ictimefilter', e => this.handleICTimeFilter(e));
this.#view.icPanel.addEventListener(
'mapclick', e => this.handleMapClick(e));
this.#view.mapPanel.addEventListener(
'click', e => this.handleMapAddressSearch(e));
this.#view.mapPanel.addEventListener(
'change', e => this.handleShowMapsChange(e));
this.#view.icPanel.addEventListener(
'filepositionclick', e => this.handleFilePositionClick(e));
}
handleMapClick(e) {
//TODO(zcankara) Direct the event based on the key and value
console.log("map: ", e.detail.key);
handleShowEntries(e){
if(e.entries[0] instanceof V8Map){
this.#view.mapPanel.mapEntries = e.entries;
}
}
handleFilePositionClick(e) {
//TODO(zcankara) Direct the event based on the key and value
console.log("filePosition: ", e.detail.key);
handleShowEntryDetail(e){
if(e.entry instanceof V8Map){
this.selectMapLogEvent(e.entry);
}
else if(e.entry instanceof Entry){
this.selectICLogEvent(e.entry);
}
else if(typeof e.entry === 'string'){
this.selectSourcePositionEvent(e.entry);
}
else {
console.log("undefined");
}
}
handleClickSourcePositions(e){
//TODO(zcankara) Handle source position
console.log("source position map detail: ", e.detail);
console.log("Entry containing source position: ", e.entries);
}
handleDblClickSelectMap(e){
//TODO(zcankara) Handle double clicked map
console.log("double clicked map: ", e.detail);
selectMapLogEvent(entry){
this.#state.map = entry;
this.#view.mapTrack.selectedEntry = entry;
this.#view.mapPanel.map = entry;
}
handleMapChange(e){
if (!(e.detail instanceof V8Map)){
console.error("selected entry not a V8Map instance");
return;
}
this.#state.map = e.detail;
this.#view.mapTrack.selectedEntry = e.detail;
this.#view.mapPanel.map = e.detail;
selectICLogEvent(entry){
console.log("IC Entry selection");
}
handleShowMaps(e){
if (!(e.detail instanceof Chunk)){
console.error("Chunk not selected");
return;
}
this.#view.mapPanel.mapEntries = e.detail.filter();
selectSourcePositionEvent(sourcePositions){
console.log("source positions: ", sourcePositions);
}
handleICTimeFilter(event) {
this.#state.timeSelection.start = event.detail.startTime;
@ -91,29 +81,15 @@ class App {
this.#state.timeSelection.end);
this.#view.icPanel.filteredEntries = this.#view.icTrack.data.selection;
}
handleMapAddressSearch(e) {
//TODO(zcankara) Combine with handleMapChange into selectMap event
//TODO(zcankara) Possibility of select no map
if(!e.detail.map) return;
this.#state.map = e.detail.map;
this.#view.mapTrack.selectedEntry = e.detail.map;
this.#view.mapPanel.map = e.detail.map;
}
handleShowMapsChange(e) {
//TODO(zcankara) Map entries repeats "map". Probabluy not needed
this.#view.mapPanel.mapEntries = e.detail;
}
handleFileUpload(e){
//TODO(zcankara) Set a state on the document.body. Exe: .loading, .loaded
$('#container').style.display = 'none';
}
// Map event log processing
handleLoadTextMapProcessor(text) {
let mapProcessor = new MapProcessor();
return mapProcessor.processString(text);
}
// IC event file reading and log processing
loadICLogFile(fileData) {
let reader = new FileReader();

View File

@ -4,6 +4,7 @@
import "./stats-panel.mjs";
import "./map-panel/map-details.mjs";
import "./map-panel/map-transitions.mjs";
import {SelectEvent} from './events.mjs';
import {V8Map} from "./map-processor.mjs";
import {defineCustomElement, V8CustomElement} from './helper.mjs';
@ -68,21 +69,16 @@ defineCustomElement('map-panel', (templateText) =>
}
handleSearchBar(e){
let dataModel = Object.create(null);
let searchBar = this.$('#searchBarInput');
let searchBarInput = searchBar.value;
//access the map from model cache
let selectedMap = V8Map.get(searchBarInput);
if(selectedMap){
dataModel.isValidMap = true;
dataModel.map = selectedMap;
searchBar.className = "success";
} else {
dataModel.isValidMap = false;
searchBar.className = "failure";
}
this.dispatchEvent(new CustomEvent(
'click', {bubbles: true, composed: true, detail: dataModel}));
this.dispatchEvent(new SelectEvent(selectedMap));
}
set mapEntries(list){

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {V8CustomElement, defineCustomElement} from '../helper.mjs';
import {SelectEvent} from '../events.mjs';
defineCustomElement('./map-panel/map-details', (templateText) =>
class MapDetails extends V8CustomElement {
@ -30,8 +31,6 @@ defineCustomElement('./map-panel/map-details', (templateText) =>
}
handleClickSourcePositions(){
this.dispatchEvent(new CustomEvent(
'sourcepositionsclick', {bubbles: true, composed: true, detail: this.selectedMap.filePosition}));
this.dispatchEvent(new SelectEvent(this.selectedMap.filePosition));
}
});

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {V8CustomElement, defineCustomElement} from '../helper.mjs';
import {SelectEvent} from '../events.mjs';
defineCustomElement('./map-panel/map-transitions', (templateText) =>
class MapTransitions extends V8CustomElement {
@ -45,13 +46,11 @@ defineCustomElement('./map-panel/map-transitions', (templateText) =>
this.currentMap = map;
this.dispatchEvent(new CustomEvent(
'mapdetailsupdate', {bubbles: true, composed: true, detail: map}));
this.dispatchEvent(new CustomEvent(
'mapchange', {bubbles: true, composed: true, detail: map}));
this.dispatchEvent(new SelectEvent(map));
}
dblClickSelectMap(map) {
this.dispatchEvent(new CustomEvent(
'selectmapdblclick', {bubbles: true, composed: true, detail: map}));
this.dispatchEvent(new SelectEvent(map));
}
showMap() {

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {V8CustomElement, defineCustomElement} from './helper.mjs';
import {SelectionEvent} from './events.mjs';
defineCustomElement('stats-panel', (templateText) =>
class StatsPanel extends V8CustomElement {
@ -88,8 +89,7 @@ defineCustomElement('stats-panel', (templateText) =>
if (node.maps == undefined) {
node.maps = this.filterUniqueTransitions(filter);
}
this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: node.maps}));
this.dispatchEvent(new SelectionEvent(node.maps));
};
row.appendChild(this.td(name));
let count = this.timeline.count(filter);
@ -113,9 +113,8 @@ defineCustomElement('stats-panel', (templateText) =>
row.maps = maps;
row.addEventListener(
'click',
e => this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true,
detail: e.target.parentNode.maps.map(map => map.to)})));
e => this.dispatchEvent(
new SelectionEvent(e.target.parentNode.maps.map(map => map.to))));
row.appendChild(this.td(name));
row.appendChild(this.td(maps.length));
tableNode.appendChild(row);

View File

@ -5,6 +5,7 @@
import {defineCustomElement, V8CustomElement,
transitionTypeToColor, CSSColor} from '../helper.mjs';
import {kChunkWidth, kChunkHeight} from '../map-processor.mjs';
import {SelectionEvent, SelectEvent} from '../events.mjs';
defineCustomElement('./timeline/timeline-track', (templateText) =>
class TimelineTrack extends V8CustomElement {
@ -107,12 +108,7 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
}
handleEntryTypeDblClick(e){
let selectedEvent = {
entries: e.target.entries
}
this.dispatchEvent(new CustomEvent(
'selectionevent', {bubbles: true, composed: true,
detail: selectedEvent}));
this.dispatchEvent(new SelectionEvent(e.target.entries));
}
timelineIndicatorMove(offset) {
@ -230,8 +226,7 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
let relativeIndex =
Math.round(event.layerY / event.target.offsetHeight * chunk.size());
let map = chunk.at(relativeIndex);
this.dispatchEvent(new CustomEvent(
'mapchange', {bubbles: true, composed: true, detail: map}));
this.dispatchEvent(new SelectEvent(map));
}
handleChunkClick(event) {
@ -242,8 +237,8 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
this.isLocked = true;
let chunk = event.target.chunk;
if (!chunk) return;
this.dispatchEvent(new CustomEvent(
'showmaps', {bubbles: true, composed: true, detail: chunk}));
let maps = chunk.filter();
this.dispatchEvent(new SelectionEvent(maps));
}
drawOverview() {