Skip to content

Commit

Permalink
make localStorage pubsub more robust (fixes #5832)
Browse files Browse the repository at this point in the history
Safari sometimes fires the StorageEvent in the same document as well.
Move lichess.StrongSocket.sri to lichess.sri and use the unique id to
filter events.
  • Loading branch information
niklasf committed Dec 29, 2019
1 parent c2981fc commit c40772d
Show file tree
Hide file tree
Showing 18 changed files with 57 additions and 52 deletions.
2 changes: 1 addition & 1 deletion public/javascripts/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ $(function() {
...lichess.formAjax($form),
success: function() {
$form.find('.saved').fadeIn();
lichess.storage.set('reload-round-tabs', Math.random());
lichess.storage.fire('reload-round-tabs');
}
});
});
Expand Down
2 changes: 0 additions & 2 deletions public/oops/diagnostics.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@
'analyse.ceval.threads',
'analyse.ceval.hash-size',
'analyse.ceval.infinite',
'ceval.pool.start',
'ceval.fen',
'just-notified',
'push-subscribed',
'grid',
Expand Down
12 changes: 10 additions & 2 deletions ui/@types/lichess/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface Lichess {
requestIdleCallback(f: () => void): void;
dispatchEvent(el: HTMLElement | Window, eventName: string): void;
hasTouchEvents: boolean;
sri: string;
isCol1(): boolean;
storage: LichessStorageHelper;
tempStorage: LichessStorageHelper; // TODO: unused
Expand Down Expand Up @@ -47,7 +48,6 @@ interface Lichess {

// socket.js
StrongSocket: {
sri: string
(url: string, version: number, cfg: any): any;
}

Expand Down Expand Up @@ -124,14 +124,16 @@ interface LichessStorageHelper {
makeBoolean(k: string): LichessBooleanStorage;
get(k: string): string | null;
set(k: string, v: string): void;
fire(k: string, v?: string): void;
remove(k: string): void;
}

interface LichessStorage {
get(): string | null;
set(v: any): void;
remove(): void;
listen(f: (e: StorageEvent) => void): void;
listen(f: (e: LichessStorageEvent) => void): void;
fire(v?: string): void;
}

interface LichessBooleanStorage {
Expand All @@ -140,6 +142,12 @@ interface LichessBooleanStorage {
toggle(): void;
}

interface LichessStorageEvent {
sri: string;
nonce: number;
value?: string;
}

interface Window {
lichess: Lichess

Expand Down
2 changes: 1 addition & 1 deletion ui/analyse/src/study/chapterNewForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function ctrl(send: SocketSend, chapters: Prop<StudyChapterMeta[]>, setTa
d.initial = vm.initial();
d.sticky = study.vm.mode.sticky;
if (!d.pgn) send("addChapter", d);
else importPgn(study.data.id, d, study.sri);
else importPgn(study.data.id, d);
close();
setTab();
},
Expand Down
1 change: 0 additions & 1 deletion ui/analyse/src/study/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export interface StudyCtrl {
onPremoveSet(): void;
redraw: Redraw;
trans: Trans;
sri: string;
}

export type Tab = 'intro' | 'members' | 'chapters';
Expand Down
21 changes: 9 additions & 12 deletions ui/analyse/src/study/studyCtrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
const send = ctrl.socket.send;
const redraw = ctrl.redraw;

const sri: string = li.StrongSocket ? li.StrongSocket.sri : '';

const vm: StudyVm = (() => {
const isManualChapter = data.chapter.id !== data.position.chapterId;
const sticked = data.features.sticky && !ctrl.initialPath && !isManualChapter && !practiceData;
Expand Down Expand Up @@ -294,7 +292,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
return xhrReload();
}
data.position.path = position.path;
if (who && who.s === sri) return;
if (who && who.s === li.sri) return;
ctrl.userJump(position.path);
redraw();
},
Expand All @@ -310,7 +308,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
if (sticky && !vm.mode.sticky) redraw();
return;
}
if (sticky && who && who.s === sri) {
if (sticky && who && who.s === li.sri) {
data.position.path = position.path + node.id;
return;
}
Expand All @@ -331,7 +329,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
setMemberActive(who);
if (wrongChapter(d)) return;
// deleter already has it done
if (who && who.s === sri) return;
if (who && who.s === li.sri) return;
if (!ctrl.tree.pathExists(d.p.path)) return xhrReload();
ctrl.tree.deleteNodeAt(position.path);
if (vm.mode.sticky) ctrl.jump(ctrl.path);
Expand All @@ -342,7 +340,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
who = d.w;
setMemberActive(who);
if (wrongChapter(d)) return;
if (who && who.s === sri) return;
if (who && who.s === li.sri) return;
if (!ctrl.tree.pathExists(d.p.path)) return xhrReload();
ctrl.tree.promoteAt(position.path, d.toMainline);
if (vm.mode.sticky) ctrl.jump(ctrl.path);
Expand All @@ -362,7 +360,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
},
descChapter(d) {
setMemberActive(d.w);
if (d.w && d.w.s === sri) return;
if (d.w && d.w.s === li.sri) return;
if (data.chapter.id === d.chapterId) {
data.chapter.description = d.desc;
chapterDesc.set(d.desc);
Expand All @@ -371,7 +369,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
},
descStudy(d) {
setMemberActive(d.w);
if (d.w && d.w.s === sri) return;
if (d.w && d.w.s === li.sri) return;
data.description = d.desc;
studyDesc.set(d.desc);
redraw();
Expand All @@ -380,7 +378,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
setMemberActive(d.w);
if (d.s && !vm.mode.sticky) vm.behind++;
if (d.s) data.position = d.p;
else if (d.w && d.w.s === sri) {
else if (d.w && d.w.s === li.sri) {
vm.mode.write = true;
vm.chapterId = d.p.chapterId;
}
Expand All @@ -404,7 +402,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
who = d.w;
setMemberActive(who);
if (wrongChapter(d)) return;
if (who && who.s === sri) return;
if (who && who.s === li.sri) return;
ctrl.tree.setShapes(d.s, ctrl.path);
if (ctrl.path === position.path) ctrl.withCg(cg => cg.setShapes(d.s));
redraw();
Expand Down Expand Up @@ -465,7 +463,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
},
liking(d) {
data.likes = d.l.likes;
if (d.w && d.w.s === sri) data.liked = d.l.me;
if (d.w && d.w.s === li.sri) data.liked = d.l.me;
redraw();
},
following_onlines: members.inviteForm.setFollowings,
Expand Down Expand Up @@ -606,6 +604,5 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes,
}
return !!relay && relay.socketHandler(t, d);
},
sri
};
};
4 changes: 2 additions & 2 deletions ui/analyse/src/study/studyXhr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ export function practiceComplete(chapterId: string, nbMoves: number) {
});
}

export function importPgn(studyId: string, data: any, sri: string) {
export function importPgn(studyId: string, data: any) {
return $.ajax({
method: 'POST',
url: `/study/${studyId}/import-pgn?sri=${sri}`,
url: `/study/${studyId}/import-pgn?sri=${window.lichess.sri}`,
data: data,
headers
});
Expand Down
4 changes: 2 additions & 2 deletions ui/ceval/src/ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export default function(opts: CevalOpts): CevalCtrl {
opts.emit(ev, work);
if (ev.fen !== lastEmitFen) {
lastEmitFen = ev.fen;
li.storage.set('ceval.fen', ev.fen);
li.storage.fire('ceval.fen', ev.fen);
}
});

Expand Down Expand Up @@ -222,7 +222,7 @@ export default function(opts: CevalOpts): CevalCtrl {

// ask other tabs if a game is in progress
if (enabled()) {
li.storage.set('ceval.fen', 'start:' + Math.random());
li.storage.fire('ceval.fen', 'start');
li.storage.make('round.ongoing').listen(_ => {
enabled(false);
opts.redraw();
Expand Down
3 changes: 1 addition & 2 deletions ui/ceval/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ export function isEvalBetter(a: Tree.ClientEval, b?: Tree.ClientEval): boolean {
// stop when another tab starts. Listen only once here,
// as the ctrl can be instanciated several times.
// gotta do the click on the toggle to have it visually change.
window.lichess.storage.make('ceval.pool.start').listen(() => {
console.log('received ceval.pool.start');
window.lichess.storage.make('ceval.pool.start').listen(_ => {
const toggle = document.getElementById('analyse-toggle-ceval');
if (toggle && (toggle as HTMLInputElement).checked) toggle.click();
});
3 changes: 1 addition & 2 deletions ui/ceval/src/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ export class Pool {
};

start = (work: Work) => {
console.log('sending ceval.pool.start');
window.lichess.storage.set('ceval.pool.start', Date.now().toString());
window.lichess.storage.fire('ceval.pool.start');
this.getWorker().then(function(worker) {
worker.start(work);
}).catch(function(error) {
Expand Down
4 changes: 2 additions & 2 deletions ui/lobby/src/boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = function(cfg, element) {
var onFirstConnect = function() {
var gameId = getParameterByName('hook_like');
if (!gameId) return;
$.post('/setup/hook/' + lichess.StrongSocket.sri + '/like/' + gameId);
$.post('/setup/hook/' + lichess.sri + '/like/' + gameId);
lobby.setTab('real_time');
history.replaceState(null, null, '/');
};
Expand Down Expand Up @@ -266,7 +266,7 @@ module.exports = function(cfg, element) {
var poolMember = hookToPoolMember(color, $form.serializeArray());
$.modal.close();
var call = {
url: $form.attr('action').replace(/sri-placeholder/, lichess.StrongSocket.sri),
url: $form.attr('action').replace(/sri-placeholder/, lichess.sri),
data: $form.serialize() + "&color=" + color,
type: 'post'
};
Expand Down
4 changes: 2 additions & 2 deletions ui/lobby/src/ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default class LobbyController {
this.trans = opts.trans;

this.poolInStorage = li.storage.make('lobby.pool-in');
this.poolInStorage.listen(() => { // when another tab joins a pool
this.poolInStorage.listen(_ => { // when another tab joins a pool
this.leavePool();
redraw();
});
Expand Down Expand Up @@ -186,7 +186,7 @@ export default class LobbyController {

poolIn = () => {
if (!this.poolMember) return;
this.poolInStorage.set(li.StrongSocket.sri);
this.poolInStorage.fire();
this.socket.poolIn(this.poolMember);
};

Expand Down
2 changes: 1 addition & 1 deletion ui/lobby/src/hookRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function sort(ctrl: LobbyController, hooks: Hook[]) {
}

export function init(hook: Hook) {
hook.action = hook.sri === window.lichess.StrongSocket.sri ? 'cancel' : 'join';
hook.action = hook.sri === window.lichess.sri ? 'cancel' : 'join';
hook.variant = hook.variant || 'standard';
}

Expand Down
2 changes: 1 addition & 1 deletion ui/lobby/src/xhr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function nowPlaying() {
export function anonPoolSeek(pool) {
return $.ajax({
method: 'POST',
url: '/setup/hook/' + window.lichess.StrongSocket.sri,
url: '/setup/hook/' + window.lichess.sri,
data: {
variant: 1,
timeMode: 1,
Expand Down
4 changes: 2 additions & 2 deletions ui/notify/src/ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function ctrl(opts: NotifyOpts, redraw: Redraw): Ctrl {

const readAllStorage = li.storage.make('notify-read-all');

readAllStorage.listen(() => {
readAllStorage.listen(_ => {
if (data) {
data.unread = 0;
opts.setCount(0);
Expand All @@ -25,7 +25,7 @@ export default function ctrl(opts: NotifyOpts, redraw: Redraw): Ctrl {
if (data.pager.currentPage === 1 && data.unread && opts.isVisible()) {
opts.setNotified();
data.unread = 0;
readAllStorage.set('' + Math.random()); // tell other tabs
readAllStorage.fire();
}
initiating = false;
scrolling = false;
Expand Down
10 changes: 4 additions & 6 deletions ui/round/src/cevalSub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ export function subscribe(ctrl: RoundController): void {
if (!ctrl.data.game.rated && ctrl.opts.userId) return;
// bots can cheat alright
if (ctrl.data.player.user && ctrl.data.player.user.title === 'BOT') return;
li.storage.make('ceval.fen').listen(ev => {
const v = ev.newValue;
if (!v) return;
else if (v.startsWith('start:')) return li.storage.set('round.ongoing', v);
li.storage.make('ceval.fen').listen(e => {
if (e.value === 'start') return li.storage.fire('round.ongoing');
const d = ctrl.data, step = lastStep(ctrl.data);
if (!found && step.ply > 14 && ctrl.isPlaying() &&
truncateFen(step.fen) === truncateFen(v)) {
e.value && truncateFen(step.fen) === truncateFen(e.value)) {
$.post('/jslog/' + d.game.id + d.player.id + '?n=ceval');
found = true;
}
Expand All @@ -31,5 +29,5 @@ export function subscribe(ctrl: RoundController): void {
}

export function publish(d: RoundData, move: ApiMove) {
if (d.opponent.ai) li.storage.set('ceval.fen', move.fen);
if (d.opponent.ai) li.storage.fire('ceval.fen', move.fen);
}
9 changes: 1 addition & 8 deletions ui/site/src/socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,6 @@ lichess.StrongSocket = function(url, version, settings) {
};
};

try {
const data = window.crypto.getRandomValues(new Uint8Array(9));
lichess.StrongSocket.sri = btoa(String.fromCharCode(...data)).replace(/[/+]/g, '_');
} catch(_) {
lichess.StrongSocket.sri = Math.random().toString(36).slice(2, 12);
}

lichess.StrongSocket.defaults = {
events: {
fen: function(e) {
Expand All @@ -291,7 +284,7 @@ lichess.StrongSocket.defaults = {
}
},
params: {
sri: lichess.StrongSocket.sri
sri: lichess.sri
},
options: {
name: "unnamed",
Expand Down
20 changes: 17 additions & 3 deletions ui/site/src/standalones/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ lichess.dispatchEvent = (el, eventName) => el.dispatchEvent(new Event(eventName)

lichess.hasTouchEvents = 'ontouchstart' in window;

try {
const data = window.crypto.getRandomValues(new Uint8Array(9));
lichess.sri = btoa(String.fromCharCode(...data)).replace(/[/+]/g, '_');
} catch(_) {
lichess.sri = Math.random().toString(36).slice(2, 12);
}

lichess.isCol1 = (() => {
let isCol1Cache = 'init'; // 'init' | 'rec' | boolean
return () => {
Expand All @@ -28,15 +35,22 @@ lichess.isCol1 = (() => {
const api = {
get: k => storage.getItem(k),
set: (k, v) => storage.setItem(k, v),
fire: (k, v) => storage.setItem(k, JSON.stringify({sri: lichess.sri, nonce: Math.random(), value: v})),
remove: k => storage.removeItem(k),
make: k => ({
get: () => api.get(k),
set: v => api.set(k, v),
fire: v => api.fire(k, v),
remove: () => api.remove(k),
listen: f => window.addEventListener('storage', e => {
if (e.key === k &&
e.storageArea === storage &&
e.newValue !== null) f(e);
if (e.key !== k || e.storageArea !== storage || e.newValue === null) return;
let parsed;
try {
parsed = JSON.parse(e.newValue);
} catch(_) {
return;
}
if (parsed.sri && parsed.sri !== lichess.sri) f(parsed);
})
}),
makeBoolean: k => ({
Expand Down

0 comments on commit c40772d

Please sign in to comment.