From 181a4efd8b32021f287b5267bf700a1b18d42fca Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Fri, 8 Jan 2021 15:13:45 +0100
Subject: [PATCH] Fix race conditions and multiple tabs on the same worker

---
 www/common/outer/async-store.js   | 24 ++++++++++++++++-----
 www/common/outer/sharedworker.js  | 35 ++++++++++++++++++-------------
 www/common/sframe-common-outer.js |  5 -----
 www/common/sframe-common.js       |  4 ----
 4 files changed, 39 insertions(+), 29 deletions(-)

diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index 0e9fbedd9..41a1d00aa 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -35,6 +35,7 @@ define([
              Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) {
 
     var onReadyEvt = Util.mkEvent(true);
+    var onCacheReadyEvt = Util.mkEvent(true);
 
     // Default settings for new users
     var NEW_USER_SETTINGS = {
@@ -2651,7 +2652,8 @@ define([
                 Feedback.init(returned.feedback);
 
                 // "cb" may have already been called by onCacheReady
-                if (typeof(cb) === 'function') { cb(returned); }
+                store.returned = returned;
+                if (typeof(cb) === 'function') { cb(); }
 
                 store.offline = false;
                 sendDriveEvent('NETWORK_RECONNECT'); // Tell inner that we're now online
@@ -2766,6 +2768,7 @@ define([
                 // Check if we can connect
                 var to = setTimeout(function () {
                     console.error('TO');
+                    store.networkTimeout = true;
                     broadcast([], "LOADING_DRIVE", {
                         type: "offline"
                     });
@@ -2785,8 +2788,10 @@ define([
 
                 onCacheReady(clientId, function () {
                     if (typeof(cb) === "function") { cb(returned); }
+                    onCacheReadyEvt.fire();
                 });
             }).on('ready', function (info) {
+                delete store.networkTimeout;
                 if (store.ready) { return; } // the store is already ready, it is a reconnection
                 store.driveMetadata = info.metadata;
                 if (!rt.proxy.drive || typeof(rt.proxy.drive) !== 'object') { rt.proxy.drive = {}; }
@@ -2875,15 +2880,26 @@ define([
 
         Store.init = function (clientId, data, _callback) {
             var callback = Util.once(_callback);
-            if (!store.returned && data.cache && store.cacheReturned) {
-                return void onCacheReady(clientId, function () {
+
+            // If this is not the first tab and we're offline, callback only if the app
+            // supports offline mode
+            if (initialized && !store.returned && data.cache) {
+                return void onCacheReadyEvt.reg(function () {
                     callback({
                         state: 'ALREADY_INIT',
                         returned: store.cacheReturned
                     });
                 });
             }
+
+            // If this is not the first tab (initialized is true), it means either we don't
+            // support offline or we're already online
             if (initialized) {
+                if (store.networkTimeout) {
+                    postMessage(clientId, "LOADING_DRIVE", {
+                        type: "offline"
+                    });
+                }
                 return void whenReady(function () {
                     callback({
                         state: 'ALREADY_INIT',
@@ -2906,8 +2922,6 @@ define([
                 }
                 if (ret && ret.error) {
                     initialized = false;
-                } else {
-                    store.returned = ret;
                 }
 
                 callback(ret);
diff --git a/www/common/outer/sharedworker.js b/www/common/outer/sharedworker.js
index a89376407..825deb383 100644
--- a/www/common/outer/sharedworker.js
+++ b/www/common/outer/sharedworker.js
@@ -73,6 +73,7 @@ var init = function (client, cb) {
                     });
                     chan.on('CONNECT', function (cfg, cb) {
                         debug('SharedW connecting to store...');
+                        /*
                         if (self.store) {
                             debug('Store already exists!');
                             if (cfg.driveEvents) {
@@ -80,31 +81,35 @@ var init = function (client, cb) {
                             }
                             return void cb(self.store);
                         }
+                        */
 
-                        debug('Loading new async store');
-                        // One-time initialization (init async-store)
-                        cfg.query = function (cId, cmd, data, cb) {
-                            cb = cb || function () {};
-                            self.tabs[cId].chan.query(cmd, data, function (err, data2) {
-                                if (err) { return void cb({error: err}); }
-                                cb(data2);
-                            });
-                        };
-                        cfg.broadcast = function (excludes, cmd, data, cb) {
-                            cb = cb || function () {};
-                            Object.keys(self.tabs).forEach(function (cId) {
-                                if (excludes.indexOf(cId) !== -1) { return; }
+                        if (!self.store) {
+                            debug('Loading new async store');
+                            // One-time initialization (init async-store)
+                            cfg.query = function (cId, cmd, data, cb) {
+                                cb = cb || function () {};
                                 self.tabs[cId].chan.query(cmd, data, function (err, data2) {
                                     if (err) { return void cb({error: err}); }
                                     cb(data2);
                                 });
-                            });
-                        };
+                            };
+                            cfg.broadcast = function (excludes, cmd, data, cb) {
+                                cb = cb || function () {};
+                                Object.keys(self.tabs).forEach(function (cId) {
+                                    if (excludes.indexOf(cId) !== -1) { return; }
+                                    self.tabs[cId].chan.query(cmd, data, function (err, data2) {
+                                        if (err) { return void cb({error: err}); }
+                                        cb(data2);
+                                    });
+                                });
+                            };
+                        }
                         Rpc.queries['CONNECT'](clientId, cfg, function (data) {
                             if (cfg.driveEvents) {
                                 Rpc._subscribeToDrive(clientId);
                             }
                             if (data && data.state === "ALREADY_INIT") {
+                                debug('Store already exists!');
                                 self.store = data.returned;
                                 return void cb(data.returned);
                             }
diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js
index e2bb8077c..1d5597ab2 100644
--- a/www/common/sframe-common-outer.js
+++ b/www/common/sframe-common-outer.js
@@ -498,11 +498,6 @@ define([
                             // We've received a link without /p/ and it doesn't work without a password: abort
                             return void todo();
                         }
-                        if (e === "ANON_RPC_NOT_READY") {
-                            // We're currently offline and the pad is not in our cache
-                            w.abort();
-                            return void sframeChan.event('EV_OFFLINE');
-                        }
                         // Wrong password or deleted file?
                         askPassword(true, passwordCfg);
                     }));
diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js
index 7fa303422..8a3328d8a 100644
--- a/www/common/sframe-common.js
+++ b/www/common/sframe-common.js
@@ -772,10 +772,6 @@ define([
                 UI.errorLoadingScreen(Messages.restrictedError);
             });
 
-            ctx.sframeChan.on("EV_OFFLINE", function () {
-                UI.errorLoadingScreen(Messages.offlineNoCacheError);
-            });
-
             ctx.sframeChan.on("EV_PAD_PASSWORD_ERROR", function () {
                 UI.errorLoadingScreen(Messages.password_error_seed);
             });