// 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 | m.ComponentTypes >; export class PageManagerImpl { private readonly registry = new Registry((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 | m.Vnode { 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, { subpage, trace: assertExists(trace), }); } return m(handler.page, {subpage, trace}); } }