blob: 18e685825b4289b61a239aeb84af3cd4a4db3904 [file] [log] [blame]
// Copyright (C) 2024 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 m from 'mithril';
import {assertExists, assertTrue} from '../base/logging';
import {Registry} from '../base/registry';
import {PageAttrs, PageHandler, PageWithTraceAttrs} from '../public/page';
import {Router} from './router';
import {TraceImpl} from './trace_impl';
export interface PageWithTraceImplAttrs extends PageAttrs {
trace: TraceImpl;
}
// This is to allow internal core classes to get a TraceImpl injected rather
// than just a Trace.
type PageHandlerInternal = PageHandler<
| m.ComponentTypes<PageWithTraceAttrs>
| m.ComponentTypes<PageWithTraceImplAttrs>
>;
export class PageManagerImpl {
private readonly registry = new Registry<PageHandlerInternal>((x) => x.route);
registerPage(pageHandler: PageHandlerInternal): Disposable {
assertTrue(/^\/\w*$/.exec(pageHandler.route) !== null);
// The pluginId is injected by the proxy in AppImpl / TraceImpl. If this is
// undefined somebody (tests) managed to call this method without proxy.
assertExists(pageHandler.pluginId);
return this.registry.register(pageHandler);
}
// Called by index.ts upon the main frame redraw callback.
renderPageForCurrentRoute(
trace: TraceImpl | undefined,
): m.Vnode<PageAttrs> | m.Vnode<PageWithTraceImplAttrs> {
const route = Router.parseFragment(location.hash);
const res = this.renderPageForRoute(trace, route.page, route.subpage);
if (res !== undefined) {
return res;
}
// If either the route doesn't exist or requires a trace but the trace is
// not loaded, fall back on the default route /.
return assertExists(this.renderPageForRoute(trace, '/', ''));
}
// Will return undefined if either: (1) the route does not exist; (2) the
// route exists, it requires a trace, but there is no trace loaded.
private renderPageForRoute(
coreTrace: TraceImpl | undefined,
page: string,
subpage: string,
) {
const handler = this.registry.tryGet(page);
if (handler === undefined) {
return undefined;
}
const pluginId = assertExists(handler?.pluginId);
const trace = coreTrace?.forkForPlugin(pluginId);
const traceRequired = !handler?.traceless;
if (traceRequired && trace === undefined) {
return undefined;
}
if (traceRequired) {
return m(handler.page as m.ComponentTypes<PageWithTraceImplAttrs>, {
subpage,
trace: assertExists(trace),
});
}
return m(handler.page, {subpage, trace});
}
}