[FIX] bus, web: clean up event handlers and message ports correctly
This commit fixes two things: - The owl compatibility layer adds an `on` and `off` method on owl's EventBus so that it can keep being used as it used to in owl 1. To do this, it needs to keep track of the the callbacks that are attached to it and by whom. To do this it keeps a Map where the "owners" are keys. When calling `off` to remove event listeners, we remove the callbacks but did not remove the owner from the Map, causing the EventBus to always hold a strong reference to any object that was given as the owner to the `on` method, preventing the item from being garbage collected. Objects that do this are typically components, and components hold a reference to their owl application that contains the entire tree of components. This was particularly problematic in tests where components are created and destroyed at a rapid pace. - The mock websocket creates a MessageChannel so that it can mock the server side of the websocket, but it never closes that MessageChannel's MessagePorts, which can cause garbage collection to be slow to happen [1] (in practice it looks like these are basically never garbage collected unless closed). This commit registers a cleanup to close the message ports after the execution of the current test. [1]: https://html.spec.whatwg.org/dev/web-messaging.html#ports-and-garbage-collection closes odoo/odoo#108309 X-original-commit: 4facc396a4e8036592a473002253d5a5daf41a19 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com> Co-authored-by: Aaron Bohy <aab@odoo.com>
This commit is contained in:
co-authored by
Aaron Bohy
parent
ea7ef13b0f
commit
78188249ca
@@ -73,7 +73,12 @@ export function patchWebsocketWorkerWithCleanup(params = {}) {
|
||||
websocketWorker = websocketWorker || new WebsocketWorker('wss://odoo.com/websocket');
|
||||
patchWithCleanup(browser, {
|
||||
SharedWorker: function () {
|
||||
return new SharedWorkerMock(websocketWorker);
|
||||
const sharedWorker = new SharedWorkerMock(websocketWorker);
|
||||
registerCleanup(() => {
|
||||
sharedWorker._messageChannel.port1.close();
|
||||
sharedWorker._messageChannel.port2.close();
|
||||
});
|
||||
return sharedWorker;
|
||||
},
|
||||
}, { pure: true });
|
||||
registerCleanup(() => {
|
||||
|
||||
@@ -14,20 +14,29 @@
|
||||
}
|
||||
on(type, target, callback) {
|
||||
if (!this.targetsCallbacks.has(target)) {
|
||||
this.targetsCallbacks.set(target, []);
|
||||
this.targetsCallbacks.set(target, {});
|
||||
}
|
||||
callback = wrapCallback(target, callback);
|
||||
this.targetsCallbacks.get(target).push(callback);
|
||||
const listeners = this.targetsCallbacks.get(target);
|
||||
if (!listeners[type]) {
|
||||
listeners[type] = new Set();
|
||||
}
|
||||
listeners[type].add(callback);
|
||||
return this.addEventListener(type, callback);
|
||||
}
|
||||
off(type, target) {
|
||||
const cbs = this.targetsCallbacks.get(target);
|
||||
if (!cbs) {
|
||||
const listeners = this.targetsCallbacks.get(target);
|
||||
if (!listeners || !Object.hasOwnProperty.call(listeners, type)) {
|
||||
return;
|
||||
}
|
||||
const cbs = listeners[type];
|
||||
for (const callback of cbs) {
|
||||
this.removeEventListener(type, callback);
|
||||
}
|
||||
delete cbs[type];
|
||||
if (Object.keys(cbs).length === 0) {
|
||||
this.targetsCallbacks.delete(target);
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user