Skip to content

Commit

Permalink
Added a mutex lock on the groups object in a hope it fixed random del…
Browse files Browse the repository at this point in the history
…etion of all group data. (#141)
  • Loading branch information
photodiode committed Mar 31, 2023
1 parent 70f8773 commit ef2bd2a
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 12 deletions.
80 changes: 68 additions & 12 deletions src/background/addon.tabGroups.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import * as core from './core.js'
import * as addon_tabs from './addon.tabs.js'
import * as backup from './backup.js'

import Mutex from '/common/mutex.js'

// internal
let groupUuid = 0;
let groups = []; // cache
let groupLock = new Mutex();

function newGroupId() {
return groupUuid += 1;
Expand Down Expand Up @@ -40,7 +43,9 @@ function sanitizeGroup(group) {
// ----

export async function setActiveId(windowId, groupId) {
let unlock = await groupLock.lock();
if (!groups.find(group => group.id == groupId)) return undefined;
await unlock();
return browser.sessions.setWindowValue(windowId, 'activeGroup', groupId);
}

Expand Down Expand Up @@ -98,9 +103,13 @@ export async function create(info = {}, currentWindowId) {
lastAccessed: (new Date).getTime(), // temporary
};

let unlock = await groupLock.lock();

groups.push(group);
await saveGroups();

await unlock();

await setActiveId(group.windowId, group.id);

if (info.hasOwnProperty('populate') && info.populate == true) {
Expand All @@ -114,7 +123,9 @@ export async function create(info = {}, currentWindowId) {
}

export async function get(groupId) {
let unlock = await groupLock.lock();
const group = groups.find(group => group.id == groupId);
await unlock();
if (!group) throw Error(`Invalid group ID: ${groupId}`);
return sanitizeGroup(group);
}
Expand All @@ -126,7 +137,9 @@ export async function query(info = {}, currentWindowId) {
info.windowId = currentWindowId;
}

let unlock = await groupLock.lock();
let matchingGroups = groups;
await unlock();

if (info.hasOwnProperty('windowId')) {
matchingGroups = matchingGroups.filter(group => group.windowId == info.windowId);
Expand All @@ -146,8 +159,13 @@ export async function query(info = {}, currentWindowId) {

export async function remove(groupId) {

let unlock = await groupLock.lock();
const group = groups.find(group => group.id == groupId);
if (!group) throw Error(`Invalid group ID: ${groupId}`);
await unlock();

if (!group) {
throw Error(`Invalid group ID: ${groupId}`);
}

// remove tabs in group
const tabs = await browser.tabs.query({currentWindow: true});
Expand All @@ -164,32 +182,43 @@ export async function remove(groupId) {
// check if tabs were removed and abort if not (beforeunload was called or something)
for (const tabId of tabsToRemove) {
try {
let tab = await browser.tabs.get(tabId);
tab = await browser.tabs.get(tabId);
return undefined;
} catch (error) {
// all good, tab got removed
// all good, tab was removed
}
}
// ----

unlock = await groupLock.lock();

groups = groups.filter(_group => _group.id != group.id);
if ((await query({currentWindow: true}, group.windowId)).length == 0) {
await create({windowId: group.windowId});
} else {
await saveGroups();
}
await saveGroups();

const windowGroups = groups.find(_group => _group.windowId == group.windowId);

await unlock();

const sending = browser.runtime.sendMessage({event: 'browser.tabGroups.onRemoved', groupId: groupId, removeInfo: {windowId: group.windowId}});
sending.catch(error => {});

if (windowGroups == undefined) {
await create({windowId: group.windowId});
}

return groupId;
}


export async function update(groupId, info = {}) {

let unlock = await groupLock.lock();

let group = groups.find(group => group.id == groupId);
if (!group) throw Error(`Invalid group ID: ${groupId}`);
if (!group) {
await unlock();
throw Error(`Invalid group ID: ${groupId}`);
}

if (info.hasOwnProperty('title')) {
group.title = info.title;
Expand All @@ -203,6 +232,8 @@ export async function update(groupId, info = {}) {

await saveGroups();

await unlock();

const sending = browser.runtime.sendMessage({event: 'browser.tabGroups.onUpdated', group: sanitizeGroup(group)});
sending.catch(error => {});

Expand All @@ -214,8 +245,13 @@ export async function setGroupValue(groupId, key, value, appId) {

value = JSON.stringify(value);

let unlock = await groupLock.lock();

let group = groups.find(group => group.id == groupId);
if (!group) throw Error(`Invalid group ID: ${groupId}`);
if (!group) {
await unlock();
throw Error(`Invalid group ID: ${groupId}`);
}

if (!group.hasOwnProperty('sessionStorage')) {
group.sessionStorage = {};
Expand All @@ -229,14 +265,21 @@ export async function setGroupValue(groupId, key, value, appId) {

await saveGroups();

await unlock();

return;
}


export async function getGroupValue(groupId, key, appId) {

let unlock = await groupLock.lock();

let group = groups.find(group => group.id == groupId);
if (!group) throw Error(`Invalid group ID: ${groupId}`);
if (!group) {
await unlock();
throw Error(`Invalid group ID: ${groupId}`);
}

if (group.hasOwnProperty('sessionStorage') &&
group.sessionStorage.hasOwnProperty(appId) &&
Expand All @@ -249,17 +292,26 @@ export async function getGroupValue(groupId, key, appId) {
value = undefined;
}

await unlock();

return value;
}

await unlock();

return undefined;
}


export async function removeGroupValue(groupId, key, appId) {

let unlock = await groupLock.lock();

let group = groups.find(group => group.id == groupId);
if (!group) throw Error(`Invalid group ID: ${groupId}`);
if (!group) {
await unlock();
throw Error(`Invalid group ID: ${groupId}`);
}

if (group.hasOwnProperty('sessionStorage') &&
group.sessionStorage.hasOwnProperty(appId) &&
Expand All @@ -269,8 +321,12 @@ export async function removeGroupValue(groupId, key, appId) {

await saveGroups();

await unlock();

return;
}

await unlock();

throw Error(`Invalid key: ${key}`);
}
29 changes: 29 additions & 0 deletions src/common/mutex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

'use strict';

class Mutex {
constructor() {
this._locking = Promise.resolve();
this._locked = false;
}

isLocked() {
return this._locked;
}

lock() {
this._locked = true;

let unlockNext;
let willLock = new Promise(resolve => unlockNext = resolve);

willLock.then(() => this._locked = false);

let willUnlock = this._locking.then(() => unlockNext);
this._locking = this._locking.then(() => willLock);

return willUnlock;
}
}

export default Mutex;

0 comments on commit ef2bd2a

Please sign in to comment.