From f93e294fff65f15b68faf2f2dcaea9893fb22cc0 Mon Sep 17 00:00:00 2001 From: Nicolas Bayet Date: Thu, 2 Mar 2023 14:50:25 +0000 Subject: [PATCH] [FIX] web_editor: prevent corruption from stale document When a peer "A" connect to the peer to peer network, if he receive a stale document from a peer "B", peer "A" will never be able to save. This situation can make impossible for any new peer to receive a converging document and block the ability to save until every peer disconnect from the network. task-3196778 closes odoo/odoo#114268 X-original-commit: 537f34f764fae3967496d466c420d72d172b241d Signed-off-by: David Monjoie (dmo) --- .../static/src/js/wysiwyg/wysiwyg.js | 67 +++++++------------ 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js index ad148ca5407..a969d2da19b 100644 --- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js +++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js @@ -519,7 +519,8 @@ const Wysiwyg = Widget.extend({ break; case 'rtc_data_channel_open': { fromClientId = notificationPayload.connectionClientId; - this.ptp.clientsInfos[fromClientId].startTime = await this.ptp.requestClient(fromClientId, 'get_start_time', undefined, { transport: 'rtc' }); + const remoteStartTime = await this.ptp.requestClient(fromClientId, 'get_start_time', undefined, { transport: 'rtc' }); + this.ptp.clientsInfos[fromClientId].startTime = remoteStartTime; this.ptp.requestClient(fromClientId, 'get_client_name', undefined, { transport: 'rtc' }).then((clientName) => { this.ptp.clientsInfos[fromClientId].clientName = clientName; }); @@ -528,8 +529,30 @@ const Wysiwyg = Widget.extend({ }); if (!historySyncAtLeastOnce) { - historySyncAtLeastOnce = true; - await editorCollaborationOptions.onHistoryNeedSync(); + const localClient = { id: this._currentClientId, startTime: this._startCollaborationTime }; + const remoteClient = { id: fromClientId, startTime: remoteStartTime }; + if (isClientFirst(localClient, remoteClient)) { + historySyncAtLeastOnce = true; + } else { + const { steps, historyIds } = await this.ptp.requestClient(fromClientId, 'get_history_from_snapshot', undefined, { transport: 'rtc' }); + // Ensure that the history hasn't been + // synced by another client before this + // `get_history_from_snapshot` finished. + if (historySyncAtLeastOnce) { + return; + } + const firstStepId = this.odooEditor.historyGetBranchIds()[0]; + const staleDocument = !historyIds.includes(firstStepId); + if (staleDocument) { + return false; + } + historySyncAtLeastOnce = true; + this.odooEditor.historyResetFromSteps(steps, historyIds); + const remoteSelection = await this.ptp.requestClient(fromClientId, 'get_collaborative_selection', undefined, { transport: 'rtc' }); + if (remoteSelection) { + this.odooEditor.onExternalMultiselectionUpdate(remoteSelection); + } + } historySyncFinished = true; } else { const remoteSelection = await this.ptp.requestClient(fromClientId, 'get_collaborative_selection', undefined, { transport: 'rtc' }); @@ -646,44 +669,6 @@ const Wysiwyg = Widget.extend({ } this.ptp && this.odooEditor.onExternalHistorySteps(missingSteps.concat([step])); }, - onHistoryNeedSync: async () => { - if (!this.ptp) return; - let firstClientId = this._currentClientId; - let firstClientStartTime = this._startCollaborationTime; - const connectedClientIds = this.ptp.getConnectedClientIds(); - for (const clientId of connectedClientIds) { - const clientInfo = this.ptp.clientsInfos[clientId]; - // Ensure we already retreived remote client starting time. - if (!clientInfo.startTime) { - continue; - } - - const isCurrentClientFirst = isClientFirst( - { - startTime: clientInfo.startTime, - id: clientId, - }, - { - startTime: firstClientStartTime, - id: firstClientId, - } - ); - - if (isCurrentClientFirst) { - firstClientStartTime = clientInfo.startTime; - firstClientId = clientId; - } - } - - if (firstClientId !== this._currentClientId) { - const { steps, historyIds } = await this.ptp.requestClient(firstClientId, 'get_history_from_snapshot', undefined, { transport: 'rtc' }); - this.odooEditor.historyResetFromSteps(steps, historyIds); - const remoteSelection = await this.ptp.requestClient(firstClientId, 'get_collaborative_selection', undefined, { transport: 'rtc' }); - if (remoteSelection) { - this.odooEditor.onExternalMultiselectionUpdate(remoteSelection); - } - } - }, }; return editorCollaborationOptions; },