Merge "ui: Backport fix for Track outliving its state"
diff --git a/ui/src/base/remote.ts b/ui/src/base/remote.ts
deleted file mode 100644
index 578bd19..0000000
--- a/ui/src/base/remote.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-// 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 {defer, Deferred} from './deferred';
-
-interface RemoteResponse {
- id: number;
- result: {};
-}
-
-/**
- * A proxy for an object that lives on another thread.
- */
-export class Remote {
- private nextRequestId: number;
- private port: MessagePort;
- // tslint:disable-next-line no-any
- private deferredRequests: Map<number, Deferred<any>>;
-
- constructor(port: MessagePort) {
- this.nextRequestId = 0;
- this.deferredRequests = new Map();
- this.port = port;
- this.port.onmessage = (event: MessageEvent) => {
- this.receive(event.data);
- };
- }
-
- /**
- * Invoke method with name |method| with |args| on the remote object.
- * Optionally set |transferList| to transfer those objects.
- */
- // tslint:disable-next-line no-any
- send<T extends any>(
- method: string,
- args: Array<{}>, transferList?: Transferable[]): Promise<T> {
- const d = defer<T>();
- this.deferredRequests.set(this.nextRequestId, d);
- const message = {
- responseId: this.nextRequestId,
- method,
- args,
- };
- if (transferList === undefined) {
- this.port.postMessage(message);
- } else {
- this.port.postMessage(message, transferList);
- }
- this.nextRequestId += 1;
- return d;
- }
-
- private receive(response: RemoteResponse): void {
- const d = this.deferredRequests.get(response.id);
- if (!d) throw new Error(`No deferred response with ID ${response.id}`);
- this.deferredRequests.delete(response.id);
- d.resolve(response.result);
- }
-}
-
-/**
- * Given a MessagePort |port| where the other end is owned by a Remote
- * (see above) turn each incoming MessageEvent into a call on |handler|
- * and post the result back to the calling thread.
- */
-export function forwardRemoteCalls(
- port: MessagePort,
- // tslint:disable-next-line no-any
- handler: {[key: string]: any}) {
- port.onmessage = (msg: MessageEvent) => {
- const method = msg.data.method;
- const id = msg.data.responseId;
- const args = msg.data.args || [];
- if (method === undefined || id === undefined) {
- throw new Error(`Invalid call method: ${method} id: ${id}`);
- }
-
- if (!(handler[method] instanceof Function)) {
- throw new Error(`Method not known: ${method}(${args})`);
- }
-
- const result = handler[method].apply(handler, args);
- const transferList = [];
-
- if (result !== undefined && result.port instanceof MessagePort) {
- transferList.push(result.port);
- }
-
- port.postMessage(
- {
- id,
- result,
- },
- transferList);
- };
-}
diff --git a/ui/src/common/worker_messages.ts b/ui/src/common/worker_messages.ts
index b60a656..b90f6b2 100644
--- a/ui/src/common/worker_messages.ts
+++ b/ui/src/common/worker_messages.ts
@@ -35,10 +35,6 @@
// the frontend <> controller interaction happens.
controllerPort: MessagePort;
- // For publishing results back to the frontend. This is used for one-way
- // non-retained publish() operations (e.g. track data after a query).
- frontendPort: MessagePort;
-
// For controller <> Chrome extension communication.
extensionPort: MessagePort;
diff --git a/ui/src/controller/globals.ts b/ui/src/controller/globals.ts
index b7784df..f479f37 100644
--- a/ui/src/controller/globals.ts
+++ b/ui/src/controller/globals.ts
@@ -15,9 +15,9 @@
import {applyPatches, Patch} from 'immer';
import {assertExists} from '../base/logging';
-import {Remote} from '../base/remote';
import {DeferredAction} from '../common/actions';
import {createEmptyState, State} from '../common/state';
+import {globals as frontendGlobals} from '../frontend/globals';
import {ControllerAny} from './controller';
export interface App {
@@ -31,24 +31,24 @@
class Globals implements App {
private _state?: State;
private _rootController?: ControllerAny;
- private _frontend?: Remote;
private _runningControllers = false;
- initialize(rootController: ControllerAny, frontendProxy: Remote) {
+ initialize(rootController: ControllerAny) {
this._rootController = rootController;
- this._frontend = frontendProxy;
this._state = createEmptyState();
}
dispatch(action: DeferredAction): void {
- this.dispatchMultiple([action]);
+ frontendGlobals.dispatch(action);
}
// Send the passed dispatch actions to the frontend. The frontend logic
// will run the actions, compute the new state and invoke patchState() so
// our copy is updated.
dispatchMultiple(actions: DeferredAction[]): void {
- assertExists(this._frontend).send<void>('dispatchMultiple', [actions]);
+ for (const action of actions) {
+ this.dispatch(action);
+ }
}
// This is called by the frontend logic which now owns and handle the
diff --git a/ui/src/controller/index.ts b/ui/src/controller/index.ts
index a84f84b..5b4bbdd 100644
--- a/ui/src/controller/index.ts
+++ b/ui/src/controller/index.ts
@@ -15,7 +15,6 @@
import '../gen/all_tracks';
import {assertTrue} from '../base/logging';
-import {Remote} from '../base/remote';
import {ControllerWorkerInitMessage} from '../common/worker_messages';
import {AppController} from './app_controller';
import {globals} from './globals';
@@ -24,12 +23,10 @@
export function initController(init: ControllerWorkerInitMessage) {
assertTrue(!initialized);
initialized = true;
- const frontendPort = init.frontendPort;
const controllerPort = init.controllerPort;
const extensionPort = init.extensionPort;
- const frontend = new Remote(frontendPort);
controllerPort.onmessage = ({data}) => globals.patchState(data);
- globals.initialize(new AppController(extensionPort), frontend);
+ globals.initialize(new AppController(extensionPort));
}
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index f3b053c..ab98c79 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -17,7 +17,6 @@
import {defer} from '../base/deferred';
import {assertExists, reportError, setErrorHandler} from '../base/logging';
-import {forwardRemoteCalls} from '../base/remote';
import {Actions, DeferredAction, StateActions} from '../common/actions';
import {initializeImmerJs} from '../common/immer_init';
import {createEmptyState, State} from '../common/state';
@@ -44,9 +43,6 @@
const EXTENSION_ID = 'lfmkphfpdbjijhpomgecfikhfohaoine';
-/**
- * The API the main thread exposes to the controller.
- */
class FrontendApi {
private port: MessagePort;
private state: State;
@@ -116,25 +112,6 @@
return patches;
}
- patchState(patches: Patch[]) {
- const oldState = globals.state;
- 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);
-
- // Only redraw if something other than the frontendLocalState changed.
- for (const key in globals.state) {
- if (key !== 'frontendLocalState' && key !== 'visibleTracks' &&
- oldState[key] !== globals.state[key]) {
- globals.rafScheduler.scheduleFullRedraw();
- return;
- }
- }
- }
-
}
function setExtensionAvailability(available: boolean) {
@@ -217,7 +194,6 @@
window.addEventListener('error', e => reportError(e));
window.addEventListener('unhandledrejection', e => reportError(e));
- const frontendChannel = new MessageChannel();
const controllerChannel = new MessageChannel();
const extensionLocalChannel = new MessageChannel();
const errorReportingChannel = new MessageChannel();
@@ -226,7 +202,6 @@
maybeShowErrorDialog(`${e.data}`);
const msg: ControllerWorkerInitMessage = {
- frontendPort: frontendChannel.port1,
controllerPort: controllerChannel.port1,
extensionPort: extensionLocalChannel.port1,
errorReportingPort: errorReportingChannel.port1,
@@ -260,8 +235,6 @@
const frontendApi = new FrontendApi(controllerChannel.port2);
globals.publishRedraw = () => globals.rafScheduler.scheduleFullRedraw();
- forwardRemoteCalls(frontendChannel.port2, frontendApi);
-
// We proxy messages between the extension and the controller because the
// controller's worker can't access chrome.runtime.
const extensionPort = window.chrome && chrome.runtime ?