diff --git a/addons/bus/static/src/js/crosstab_bus.js b/addons/bus/static/src/js/crosstab_bus.js index bb2b2a00d50..ad90bdb217b 100644 --- a/addons/bus/static/src/js/crosstab_bus.js +++ b/addons/bus/static/src/js/crosstab_bus.js @@ -46,6 +46,7 @@ var CrossTabBus = Longpolling.extend({ var now = new Date().getTime(); // used to prefix localStorage keys this._sanitizedOrigin = session.origin.replace(/:\/{0,2}/g, '_'); + this._currentTabChannels = new Set(); // prevents collisions between different tabs and in tests this._id = _.uniqueId(this.LOCAL_STORAGE_PREFIX) + ':' + now; if (this._callLocalStorage('getItem', 'last_ts', 0) + 50000 < now) { @@ -66,18 +67,20 @@ var CrossTabBus = Longpolling.extend({ * * @override */ - addChannel: function () { + addChannel: function (channel) { + this._currentTabChannels.add(channel); this._super.apply(this, arguments); - this._callLocalStorage('setItem', 'channels', this._channels); + this._updateChannels(); }, /** * Share the bus channels with the others tab by the local storage * * @override */ - deleteChannel: function () { + deleteChannel: function (channel) { + this._currentTabChannels.delete(channel); this._super.apply(this, arguments); - this._callLocalStorage('setItem', 'channels', this._channels); + this._updateChannels(); }, /** * @return {string} @@ -118,12 +121,11 @@ var CrossTabBus = Longpolling.extend({ this._heartbeat(); if (this._isMasterTab) { - this._callLocalStorage('setItem', 'channels', this._channels); this._callLocalStorage('setItem', 'options', this._options); } else { - this._channels = this._callLocalStorage('getItem', 'channels', this._channels); this._options = this._callLocalStorage('getItem', 'options', this._options); } + this._updateChannels(); return; // startPolling will be called again on tab registration } @@ -274,6 +276,45 @@ var CrossTabBus = Longpolling.extend({ this._callLocalStorage('setItem', 'peers', peers); } }, + /** + * Update localstorage channels of with the channels of this tab. + * + * @private + * @return {boolean} true if the aggregated channels has changed. + */ + _updateChannels: function () { + const currentPeerIds = new Set(Object.keys(this._callLocalStorage('getItem', 'peers') || {})); + const peerChannels = this._callLocalStorage('getItem', 'channels') || {}; + const peerChannelsBefore = JSON.stringify(peerChannels); + peerChannels[this._id] = Array.from(this._currentTabChannels); + + // Clean outdated channels. + for (const channelPeerId of Object.keys(peerChannels)) { + if (!currentPeerIds.has(channelPeerId)) { + delete peerChannels[channelPeerId]; + } + } + + const peerChannelsAfter = JSON.stringify(peerChannels); + if (peerChannelsBefore === peerChannelsAfter) { + return false; + } + this._callLocalStorage('setItem', 'channels', peerChannels); + + const allChannels = new Set(); + for (const channels of Object.values(peerChannels)) { + for (const channel of channels) { + allChannels.add(channel); + } + } + // Insure the current tab channels are always in the aggregated channels + // in case this tab is not in the currentPeerIds nor peerChannels. + for (const channel of this._currentTabChannels) { + allChannels.add(channel); + } + this._channels = Array.from(allChannels); + return true; + }, //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- @@ -327,7 +368,9 @@ var CrossTabBus = Longpolling.extend({ } // update channels else if (key === this._generateKey('channels')) { - this._channels = value; + if (this._updateChannels()) { + this._restartPolling(); + }; } // update options else if (key === this._generateKey('options')) { @@ -349,6 +392,8 @@ var CrossTabBus = Longpolling.extend({ var peers = this._callLocalStorage('getItem', 'peers') || {}; delete peers[this._id]; this._callLocalStorage('setItem', 'peers', peers); + this._currentTabChannels.clear(); + this._updateChannels(); // unload master if (this._isMasterTab) { diff --git a/addons/bus/static/src/js/longpolling_bus.js b/addons/bus/static/src/js/longpolling_bus.js index 9c8df877c9a..b9580574ee8 100644 --- a/addons/bus/static/src/js/longpolling_bus.js +++ b/addons/bus/static/src/js/longpolling_bus.js @@ -75,11 +75,7 @@ var LongpollingBus = Bus.extend(ServicesMixin, { addChannel: function (channel) { if (this._channels.indexOf(channel) === -1) { this._channels.push(channel); - if (this._pollRpc) { - this._pollRpc.abort(); - } else { - this.startPolling(); - } + this._restartPolling(); } }, /** @@ -255,6 +251,18 @@ var LongpollingBus = Bus.extend(ServicesMixin, { _onPresence: function () { this._lastPresenceTime = new Date().getTime(); }, + /** + * Restart polling. + * + * @private + */ + _restartPolling() { + if (this._pollRpc) { + this._pollRpc.abort(); + } else { + this.startPolling(); + } + }, }); return LongpollingBus;