mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-14 15:56:38 +03:00
f1c1585afb
Before, we would count on all event listeners for FakeEventTargets to be cleaned up by the object that listens. Now, FakeEventTarget implements IReleasable, so that all listeners are removed when owners call release(). For objects extending FakeEventTarget and also implementing IDestroyable, the destroy() methods will call out to super.release() to clean up listeners then. The owner should use destroy() in those cases. Issue #3949 (memory leak in DASH live streams with inband EventStream)
167 lines
4.6 KiB
JavaScript
167 lines
4.6 KiB
JavaScript
/*! @license
|
|
* Shaka Player
|
|
* Copyright 2016 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
goog.provide('shaka.util.FakeEventTarget');
|
|
|
|
goog.require('goog.asserts');
|
|
goog.require('shaka.log');
|
|
goog.require('shaka.util.FakeEvent');
|
|
goog.require('shaka.util.IReleasable');
|
|
goog.require('shaka.util.MultiMap');
|
|
|
|
|
|
/**
|
|
* @summary A work-alike for EventTarget. Only DOM elements may be true
|
|
* EventTargets, but this can be used as a base class to provide event dispatch
|
|
* to non-DOM classes. Only FakeEvents should be dispatched.
|
|
*
|
|
* @implements {EventTarget}
|
|
* @implements {shaka.util.IReleasable}
|
|
* @exportInterface
|
|
*/
|
|
shaka.util.FakeEventTarget = class {
|
|
/** */
|
|
constructor() {
|
|
/**
|
|
* @private {shaka.util.MultiMap.<shaka.util.FakeEventTarget.ListenerType>}
|
|
*/
|
|
this.listeners_ = new shaka.util.MultiMap();
|
|
|
|
/**
|
|
* The target of all dispatched events. Defaults to |this|.
|
|
* @type {EventTarget}
|
|
*/
|
|
this.dispatchTarget = this;
|
|
}
|
|
|
|
/**
|
|
* Add an event listener to this object.
|
|
*
|
|
* @param {string} type The event type to listen for.
|
|
* @param {shaka.util.FakeEventTarget.ListenerType} listener The callback or
|
|
* listener object to invoke.
|
|
* @param {(!AddEventListenerOptions|boolean)=} options Ignored.
|
|
* @override
|
|
* @exportInterface
|
|
*/
|
|
addEventListener(type, listener, options) {
|
|
if (!this.listeners_) {
|
|
return;
|
|
}
|
|
this.listeners_.push(type, listener);
|
|
}
|
|
|
|
/**
|
|
* Add an event listener to this object that is invoked for all events types
|
|
* the object fires.
|
|
*
|
|
* @param {shaka.util.FakeEventTarget.ListenerType} listener The callback or
|
|
* listener object to invoke.
|
|
* @exportInterface
|
|
*/
|
|
listenToAllEvents(listener) {
|
|
this.addEventListener(shaka.util.FakeEventTarget.ALL_EVENTS_, listener);
|
|
}
|
|
|
|
/**
|
|
* Remove an event listener from this object.
|
|
*
|
|
* @param {string} type The event type for which you wish to remove a
|
|
* listener.
|
|
* @param {shaka.util.FakeEventTarget.ListenerType} listener The callback or
|
|
* listener object to remove.
|
|
* @param {(EventListenerOptions|boolean)=} options Ignored.
|
|
* @override
|
|
* @exportInterface
|
|
*/
|
|
removeEventListener(type, listener, options) {
|
|
if (!this.listeners_) {
|
|
return;
|
|
}
|
|
this.listeners_.remove(type, listener);
|
|
}
|
|
|
|
/**
|
|
* Dispatch an event from this object.
|
|
*
|
|
* @param {!Event} event The event to be dispatched from this object.
|
|
* @return {boolean} True if the default action was prevented.
|
|
* @override
|
|
* @exportInterface
|
|
*/
|
|
dispatchEvent(event) {
|
|
// In many browsers, it is complex to overwrite properties of actual Events.
|
|
// Here we expect only to dispatch FakeEvents, which are simpler.
|
|
goog.asserts.assert(event instanceof shaka.util.FakeEvent,
|
|
'FakeEventTarget can only dispatch FakeEvents!');
|
|
|
|
if (!this.listeners_) {
|
|
return true;
|
|
}
|
|
|
|
let listeners = this.listeners_.get(event.type) || [];
|
|
const universalListeners =
|
|
this.listeners_.get(shaka.util.FakeEventTarget.ALL_EVENTS_);
|
|
if (universalListeners) {
|
|
listeners = listeners.concat(universalListeners);
|
|
}
|
|
|
|
// Execute this event on listeners until the event has been stopped or we
|
|
// run out of listeners.
|
|
for (const listener of listeners) {
|
|
// Do this every time, since events can be re-dispatched from handlers.
|
|
event.target = this.dispatchTarget;
|
|
event.currentTarget = this.dispatchTarget;
|
|
|
|
try {
|
|
// Check for the |handleEvent| member to test if this is a
|
|
// |EventListener| instance or a basic function.
|
|
if (listener.handleEvent) {
|
|
listener.handleEvent(event);
|
|
} else {
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
listener.call(this, event);
|
|
}
|
|
} catch (exception) {
|
|
// Exceptions during event handlers should not affect the caller,
|
|
// but should appear on the console as uncaught, according to MDN:
|
|
// https://mzl.la/2JXgwRo
|
|
shaka.log.error('Uncaught exception in event handler', exception,
|
|
exception ? exception.message : null,
|
|
exception ? exception.stack : null);
|
|
}
|
|
|
|
if (event.stopped) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return event.defaultPrevented;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @exportInterface
|
|
*/
|
|
release() {
|
|
this.listeners_ = null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* These are the listener types defined in the closure extern for EventTarget.
|
|
* @typedef {EventListener|function(!Event):*}
|
|
* @exportInterface
|
|
*/
|
|
shaka.util.FakeEventTarget.ListenerType;
|
|
|
|
|
|
/**
|
|
* @const {string}
|
|
* @private
|
|
*/
|
|
shaka.util.FakeEventTarget.ALL_EVENTS_ = 'All';
|