Clean event dispatching

This should remove a bunch of checking event types and make it simpler
to add new views, like messages, errors, and a timeline.

Bug:794255
Change-Id: I3ec743f9a168938b62100d4f74e0bb61174418aa
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2141699
Commit-Queue: Ted Meyer <[email protected]>
Reviewed-by: Tim van der Lippe <[email protected]>
diff --git a/front_end/media/MainView.js b/front_end/media/MainView.js
index 374ab50..c2b6910 100644
--- a/front_end/media/MainView.js
+++ b/front_end/media/MainView.js
@@ -6,12 +6,63 @@
 import * as SDK from '../sdk/sdk.js';
 import * as UI from '../ui/ui.js';
 
-import {Event, Events, MediaChangeTypeKeys, MediaModel} from './MediaModel.js';  // eslint-disable-line no-unused-vars
+import {MediaModel, PlayerEvent, ProtocolTriggers} from './MediaModel.js';  // eslint-disable-line no-unused-vars
 import {PlayerDetailView} from './PlayerDetailView.js';
 import {PlayerListView} from './PlayerListView.js';
 
+/** @interface */
+export class TriggerHandler {
+  /** @param {!Protocol.Media.PlayerProperty} property */
+  onProperty(property) {
+  }
+
+  /** @param {!Protocol.Media.PlayerError} error */
+  onError(error) {
+  }
+
+  /** @param {!Protocol.Media.PlayerMessage} message */
+  onMessage(message) {
+  }
+
+  /** @param {!PlayerEvent} event */
+  onEvent(event) {
+  }
+}
+
+/** @interface */
+export class TriggerDispatcher {
+  /**
+   * @param {string} playerID
+   * @param {!Protocol.Media.PlayerProperty} property
+   */
+  onProperty(playerID, property) {
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {!Protocol.Media.PlayerError} error
+   */
+  onError(playerID, error) {
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {!Protocol.Media.PlayerMessage} message
+   */
+  onMessage(playerID, message) {
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {!PlayerEvent} event
+   */
+  onEvent(playerID, event) {
+  }
+}
+
 /**
  * @implements {SDK.SDKModel.SDKModelObserver<!Media.MediaModel>}
+ * @implements TriggerDispatcher
  */
 export class MainView extends UI.Panel.PanelWithSidebar {
   constructor() {
@@ -32,24 +83,6 @@
 
   /**
    * @param {string} playerID
-   * @param {!Array.<!Event>} changes
-   * @param {!MediaChangeTypeKeys} changeType
-   */
-  renderChanges(playerID, changes, changeType) {
-    if (this._deletedPlayers.has(playerID)) {
-      return;
-    }
-
-    if (!this._detailPanels.has(playerID)) {
-      return;
-    }
-
-    this._sidebar.renderChanges(playerID, changes, changeType);
-    this._detailPanels.get(playerID).renderChanges(playerID, changes, changeType);
-  }
-
-  /**
-   * @param {string} playerID
    */
   renderMainPanel(playerID) {
     if (!this._detailPanels.has(playerID)) {
@@ -60,14 +93,6 @@
   }
 
   /**
-   * @param {string} playerID
-   */
-  _onPlayerCreated(playerID) {
-    this._sidebar.addMediaElementItem(playerID);
-    this._detailPanels.set(playerID, new PlayerDetailView());
-  }
-
-  /**
    * @override
    */
   wasShown() {
@@ -109,32 +134,126 @@
    */
   _addEventListeners(mediaModel) {
     mediaModel.ensureEnabled();
-    mediaModel.addEventListener(Events.PlayerPropertiesChanged, this._propertiesChanged, this);
-    mediaModel.addEventListener(Events.PlayerEventsAdded, this._eventsAdded, this);
-    mediaModel.addEventListener(Events.PlayersCreated, this._playersCreated, this);
+    mediaModel.addEventListener(ProtocolTriggers.PlayerPropertiesChanged, this._propertiesChanged, this);
+    mediaModel.addEventListener(ProtocolTriggers.PlayerEventsAdded, this._eventsAdded, this);
+    mediaModel.addEventListener(ProtocolTriggers.PlayerMessagesLogged, this._messagesLogged, this);
+    mediaModel.addEventListener(ProtocolTriggers.PlayerErrorsRaised, this._errorsRaised, this);
+    mediaModel.addEventListener(ProtocolTriggers.PlayersCreated, this._playersCreated, this);
   }
 
   /**
    * @param {!Media.MediaModel} mediaModel
    */
   _removeEventListeners(mediaModel) {
-    mediaModel.removeEventListener(Events.PlayerPropertiesChanged, this._propertiesChanged, this);
-    mediaModel.removeEventListener(Events.PlayerEventsAdded, this._eventsAdded, this);
-    mediaModel.removeEventListener(Events.PlayersCreated, this._playersCreated, this);
+    mediaModel.removeEventListener(ProtocolTriggers.PlayerPropertiesChanged, this._propertiesChanged, this);
+    mediaModel.removeEventListener(ProtocolTriggers.PlayerEventsAdded, this._eventsAdded, this);
+    mediaModel.removeEventListener(ProtocolTriggers.PlayerMessagesLogged, this._messagesLogged, this);
+    mediaModel.removeEventListener(ProtocolTriggers.PlayerErrorsRaised, this._errorsRaised, this);
+    mediaModel.removeEventListener(ProtocolTriggers.PlayersCreated, this._playersCreated, this);
+  }
+
+  /**
+   * @param {string} playerID
+   */
+  _onPlayerCreated(playerID) {
+    this._sidebar.addMediaElementItem(playerID);
+    this._detailPanels.set(playerID, new PlayerDetailView());
   }
 
   /**
    * @param {!Common.EventTarget.EventTargetEvent} event
    */
   _propertiesChanged(event) {
-    this.renderChanges(event.data.playerId, event.data.properties, MediaChangeTypeKeys.Property);
+    for (const property of event.data.properties) {
+      this.onProperty(event.data.playerId, property);
+    }
   }
 
   /**
    * @param {!Common.EventTarget.EventTargetEvent} event
    */
   _eventsAdded(event) {
-    this.renderChanges(event.data.playerId, event.data.events, MediaChangeTypeKeys.Event);
+    for (const ev of event.data.events) {
+      this.onEvent(event.data.playerId, ev);
+    }
+  }
+
+  /**
+   * @param {!Common.EventTarget.EventTargetEvent} event
+   */
+  _messagesLogged(event) {
+    for (const message of event.data.messages) {
+      this.onMessage(event.data.playerId, message);
+    }
+  }
+
+  /**
+   * @param {!Common.EventTarget.EventTargetEvent} event
+   */
+  _errorsRaised(event) {
+    for (const error of event.data.errors) {
+      this.onError(event.data.playerId, error);
+    }
+  }
+
+  /**
+   * @param {string} playerID
+   * @return {boolean}
+   */
+  _shouldPropagate(playerID) {
+    return !this._deletedPlayers.has(playerID) && this._detailPanels.has(playerID);
+  }
+
+  /**
+   * @override
+   * @param {string} playerID
+   * @param {!Protocol.Media.PlayerProperty} property
+   */
+  onProperty(playerID, property) {
+    if (!this._shouldPropagate(playerID)) {
+      return;
+    }
+    this._sidebar.onProperty(playerID, property);
+    this._detailPanels.get(playerID).onProperty(property);
+  }
+
+  /**
+   * @override
+   * @param {string} playerID
+   * @param {!Protocol.Media.PlayerError} error
+   */
+  onError(playerID, error) {
+    if (!this._shouldPropagate(playerID)) {
+      return;
+    }
+    this._sidebar.onError(playerID, error);
+    this._detailPanels.get(playerID).onError(error);
+  }
+
+  /**
+   * @override
+   * @param {string} playerID
+   * @param {!Protocol.Media.PlayerMessage} message
+   */
+  onMessage(playerID, message) {
+    if (!this._shouldPropagate(playerID)) {
+      return;
+    }
+    this._sidebar.onMessage(playerID, message);
+    this._detailPanels.get(playerID).onMessage(message);
+  }
+
+  /**
+   * @override
+   * @param {string} playerID
+   * @param {!PlayerEvent} event
+   */
+  onEvent(playerID, event) {
+    if (!this._shouldPropagate(playerID)) {
+      return;
+    }
+    this._sidebar.onEvent(playerID, event);
+    this._detailPanels.get(playerID).onEvent(event);
   }
 
   /**