From 517492f7d36cf22d3fef158045f15dee87781e4e Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 5 Nov 2020 17:30:35 +0100 Subject: [PATCH 1/3] Fix loading errors --- customize.dist/loading.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 733b3fff0..344c61942 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -317,9 +317,12 @@ button.primary:hover{ var c = types.indexOf(data.type); if (c < current) { return console.error(data); } try { - document.querySelector('.cp-loading-spinner-container').style.display = 'none'; - document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); - document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); + var el1 = document.querySelector('.cp-loading-spinner-container'); + if (el1) { el1.style.display = 'none'; } + var el2 = document.querySelector('.cp-loading-progress-list'); + if (el2) { el2.innerHTML = makeList(data); } + var el3 = document.querySelector('.cp-loading-progress-container'); + if (el3) { el3.innerHTML = makeBar(data); } } catch (e) { console.error(e); } }; window.CryptPad_updateLoadingProgress = updateLoadingProgress; From 5946b10d0bd97488544ceb69c5b1782e2ed879a6 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 6 Nov 2020 15:00:58 +0100 Subject: [PATCH 2/3] No page reload when the cache is corrupted onReady --- lib/hk-util.js | 6 ++++-- www/common/outer/cache-store.js | 4 +++- www/common/sframe-app-framework.js | 21 +++++++++++++-------- www/common/sframe-common-outer.js | 3 +-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/hk-util.js b/lib/hk-util.js index 4fa2140f9..20e0ce5d4 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -440,9 +440,11 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, _cb) => { // If our lastKnownHash is older than the 2nd to last checkpoint, // only send the last 2 checkpoints and ignore "lkh" - if (lkh && index.cpIndex.length >= 2 && lkh < index.cpIndex[0].offset) { + // XXX XXX this is probably wrong! ChainPad may not accept checkpoints that are not connected to root + // XXX We probably need to send an EUNKNOWN here so that the client can recreate a new chainpad + /*if (lkh && index.cpIndex.length >= 2 && lkh < index.cpIndex[0].offset) { return void cb(null, index.cpIndex[0].offset); - } + }*/ // Otherwise use our lastKnownHash cb(null, lkh); diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index 56c78a5be..ff2a40e93 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -55,7 +55,9 @@ define([ S.clearChannel = function (id, _cb) { cb = Util.once(Util.mkAsync(_cb || function () {})); - cache.removeItem(id, cb); + cache.removeItem(id, function () { + cb(); + }); }; S.clear = function () { diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 6353fb2f8..50565cc0b 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -467,9 +467,14 @@ define([ }); }; - var onCorruptedCache = function (cb) { - var sframeChan = common.getSframeChannel(); - sframeChan.event("Q_CORRUPTED_CACHE", cb); + var noCache = false; // Prevent reload loops + var onCorruptedCache = function () { + if (noCache) { + // XXX translation key + return UI.errorLoadingScreen("Reload loop: empty chainpad for a non-empty channel"); + } + noCache = true; + sframeChan.event("Q_CORRUPTED_CACHE"); }; var onCacheReady = function () { stateChange(STATE.DISCONNECTED); @@ -547,17 +552,17 @@ define([ Feedback.send("NON_EMPTY_NEWDOC"); // The cache may be wrong, empty it and reload after. waitFor.abort(); - UI.errorLoadingScreen("MAYBE CORRUPTED CACHE... RELOADING"); // XXX - onCorruptedCache(function () { - setTimeout(function () { common.gotoURL(); }, 1000); - }); + onCorruptedCache(); return; } - console.log('updating title'); title.updateTitle(title.defaultTitle); evOnDefaultContentNeeded.fire(); } }).nThen(function () { + // We have a valid chainpad, reenable cache fix in case with reconnect with + // a corrupted cache + noCache = false; + stateChange(STATE.READY); firstConnection = false; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 4c448e822..a53171150 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1637,8 +1637,7 @@ define([ }); }; - sframeChan.on('Q_CORRUPTED_CACHE', function (data, cb) { - Utils.Cache.clearChannel(secret.channel, cb); + sframeChan.on('EV_CORRUPTED_CACHE', function () { Cryptpad.onCorruptedCache(secret.channel); }); From 11abd6170b7affc1b3217e27a1df31e1ccf17e24 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 6 Nov 2020 15:30:56 +0100 Subject: [PATCH 3/3] Use indexeddb cache for blobs --- www/common/media-tag.js | 68 +++++++++++++++++++++++++-------- www/common/outer/cache-store.js | 22 +++++++++++ 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 27683c54e..1d7cd0c61 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -1,8 +1,5 @@ -(function(name, definition) { - if (typeof module !== 'undefined') { module.exports = definition(); } - else if (typeof define === 'function' && typeof define.amd === 'object') { define(definition); } - else { this[name] = definition(); } -}('MediaTag', function() { +(function (window) { +var factory = function (Cache) { var cache; var cypherChunkLength = 131088; @@ -133,20 +130,44 @@ cb = function () {}; }; - var xhr = new XMLHttpRequest(); - xhr.open('GET', src, true); - xhr.responseType = 'arraybuffer'; + var _src = src.replace(/(\/)*$/, ''); // Remove trailing slashes + var idx = _src.lastIndexOf('/'); + var cacheKey = _src.slice(idx+1); + if (!/^[a-f0-9]{48}$/.test(cacheKey)) { cacheKey = undefined; } - xhr.onerror = function () { return void cb("XHR_ERROR"); }; - xhr.onload = function () { - // Error? - if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); } + var fetch = function () { + var xhr = new XMLHttpRequest(); + xhr.open('GET', src, true); + xhr.responseType = 'arraybuffer'; - var arrayBuffer = xhr.response; - if (arrayBuffer) { cb(null, new Uint8Array(arrayBuffer)); } + xhr.onerror = function () { return void cb("XHR_ERROR"); }; + xhr.onload = function () { + // Error? + if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); } + + var arrayBuffer = xhr.response; + if (arrayBuffer) { + var u8 = new Uint8Array(arrayBuffer); + if (cacheKey) { + return void Cache.setBlobCache(cacheKey, u8, function (err) { + cb(null, u8); + }); + } + cb(null, u8); + } + }; + + xhr.send(null); }; - xhr.send(null); + if (!cacheKey) { return void fetch(); } + + Cache.getBlobCache(cacheKey, function (err, u8) { + if (err || !u8) { return void fetch(); } + console.error('using cache', cacheKey); + cb(null, u8); + }); + }; // Decryption tools @@ -469,4 +490,19 @@ }; return init; -})); +}; + + if (typeof(module) !== 'undefined' && module.exports) { + module.exports = factory( + require("./outer/cache-store.js") + ); + } else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) { + define([ + '/common/outer/cache-store.js', + ], function (Cache) { + return factory(Cache); + }); + } else { + // unsupported initialization + } +}(typeof(window) !== 'undefined'? window : {})); diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index ff2a40e93..fe3b541b2 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -8,6 +8,28 @@ define([ name: "cp_cache" }); + S.getBlobCache = function (id, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + cache.getItem(id, function (err, obj) { + if (err || !obj || !obj.c) { + return void cb(err || 'EINVAL'); + } + cb(null, obj.c); + obj.t = +new Date(); + cache.setItem(id, obj); + }); + }; + S.setBlobCache = function (id, u8, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + if (!u8) { return void cb('EINVAL'); } + cache.setItem(id, { + c: u8, + t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set) + }, function (err) { + cb(err); + }); + }; + // id: channel ID or blob ID // returns array of messages S.getChannelCache = function (id, cb) {