blob: 3d49d6d577b90943e67bf0f50a7de3470b7d863f [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import '../tracks/all_frontend';
import {applyPatches, Patch} from 'immer';
import * as m from 'mithril';
import {forwardRemoteCalls} from '../base/remote';
import {Actions} from '../common/actions';
import {LogBoundsKey, LogEntriesKey, LogExistsKey} from '../common/logs';
import {globals, QuantizedLoad, SliceDetails, ThreadDesc} from './globals';
import {HomePage} from './home_page';
import {openBufferWithLegacyTraceViewer} from './legacy_trace_viewer';
import {RecordPage} from './record_page';
import {Router} from './router';
import {ViewerPage} from './viewer_page';
/**
* The API the main thread exposes to the controller.
*/
class FrontendApi {
constructor(private router: Router) {}
patchState(patches: Patch[]) {
globals.state = applyPatches(globals.state, patches);
// If the visible time in the global state has been updated more recently
// than the visible time handled by the frontend @ 60fps, update it. This
// typically happens when restoring the state from a permalink.
globals.frontendLocalState.mergeState(globals.state.frontendLocalState);
this.redraw();
}
// TODO: we can't have a publish method for each batch of data that we don't
// want to keep in the global state. Figure out a more generic and type-safe
// mechanism to achieve this.
publishOverviewData(data: {[key: string]: QuantizedLoad | QuantizedLoad[]}) {
for (const [key, value] of Object.entries(data)) {
if (!globals.overviewStore.has(key)) {
globals.overviewStore.set(key, []);
}
if (value instanceof Array) {
globals.overviewStore.get(key)!.push(...value);
} else {
globals.overviewStore.get(key)!.push(value);
}
}
globals.rafScheduler.scheduleRedraw();
}
publishTrackData(args: {id: string, data: {}}) {
globals.setTrackData(args.id, args.data);
if ([LogExistsKey, LogBoundsKey, LogEntriesKey].includes(args.id)) {
globals.rafScheduler.scheduleFullRedraw();
} else {
globals.rafScheduler.scheduleRedraw();
}
}
publishQueryResult(args: {id: string, data: {}}) {
globals.queryResults.set(args.id, args.data);
this.redraw();
}
publishThreads(data: ThreadDesc[]) {
globals.threads.clear();
data.forEach(thread => {
globals.threads.set(thread.utid, thread);
});
this.redraw();
}
publishSliceDetails(click: SliceDetails) {
globals.sliceDetails = click;
this.redraw();
}
// For opening JSON/HTML traces with the legacy catapult viewer.
publishLegacyTrace(args: {data: ArrayBuffer, size: number}) {
const arr = new Uint8Array(args.data, 0, args.size);
const str = (new TextDecoder('utf-8')).decode(arr);
openBufferWithLegacyTraceViewer('trace.json', str, 0);
}
private redraw(): void {
if (globals.state.route &&
globals.state.route !== this.router.getRouteFromHash()) {
this.router.setRouteOnHash(globals.state.route);
}
globals.rafScheduler.scheduleFullRedraw();
}
}
function main() {
const controller = new Worker('controller_bundle.js');
controller.onerror = e => {
console.error(e);
};
const channel = new MessageChannel();
controller.postMessage(channel.port1, [channel.port1]);
const dispatch = controller.postMessage.bind(controller);
const router = new Router(
'/',
{
'/': HomePage,
'/viewer': ViewerPage,
'/record': RecordPage,
},
dispatch);
forwardRemoteCalls(channel.port2, new FrontendApi(router));
globals.initialize(dispatch, controller);
globals.rafScheduler.domRedraw = () =>
m.render(document.body, m(router.resolve(globals.state.route)));
// Put these variables in the global scope for better debugging.
(window as {} as {m: {}}).m = m;
(window as {} as {globals: {}}).globals = globals;
// /?s=xxxx for permalinks.
const stateHash = Router.param('s');
if (stateHash) {
globals.dispatch(Actions.loadPermalink({
hash: stateHash,
}));
}
// Prevent pinch zoom.
document.body.addEventListener('wheel', (e: MouseEvent) => {
if (e.ctrlKey) e.preventDefault();
}, {passive: false});
router.navigateToCurrentHash();
}
main();