Skip to content

Commit

Permalink
fix: recover from error during context menu setup
Browse files Browse the repository at this point in the history
  • Loading branch information
dessant committed Jun 20, 2024
1 parent 6c662c7 commit d4755b7
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 43 deletions.
62 changes: 48 additions & 14 deletions src/background/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {v4 as uuidv4} from 'uuid';
import Queue from 'p-queue';

import {initStorage} from 'storage/init';
import {isStorageReady} from 'storage/storage';
import {isStorageReady, encodeStorageData} from 'storage/storage';
import storage from 'storage/storage';
import {
getEnabledEngines,
Expand Down Expand Up @@ -131,6 +131,11 @@ async function removeMenuItem(menuItemId, {throwError = false} = {}) {
}
}

async function removeAllMenuItems() {
// removes context menu items from all instances
await browser.contextMenus.removeAll();
}

async function createMenu() {
const context = {
name: 'private',
Expand All @@ -153,13 +158,39 @@ async function createMenu() {
await createMenuItem(item);
}
} catch (err) {
// removes context menu items from all instances
await browser.contextMenus.removeAll();
// The storage may have been cleared, and the context menu state is
// no longer known. All menu items are removed and the menu is recreated.

await removeAllMenuItems();

for (const item of newItems) {
await createMenuItem(item);
}

if (runOnce('createMenuError')) {
await dispatchMenuChangeEvent();
}

throw err;
}
}

async function dispatchMenuChangeEvent() {
if (['chrome', 'edge', 'opera'].includes(targetEnv)) {
// notify the other background script instance
await storage.set(
{menuChangeEvent: Date.now()},
{
area: mv3 ? 'session' : 'local',
context: {
name: 'private',
active: !browser.extension.inIncognitoContext
}
}
);
}
}

async function getMenuItem({
id,
title = '',
Expand All @@ -178,6 +209,10 @@ async function getMenuItem({
type
};

if (browser.extension.inIncognitoContext) {
params.id += '_private';
}

if (documentUrlPatterns) {
params.documentUrlPatterns = documentUrlPatterns;
}
Expand Down Expand Up @@ -734,15 +769,9 @@ async function onActionPopupClick(engine, docUrl) {
}

async function setContextMenu() {
if (['chrome', 'edge', 'opera'].includes(targetEnv)) {
// notify all background script instances
await storage.set(
{setContextMenuEvent: Date.now()},
{area: mv3 ? 'session' : 'local'}
);
} else {
await createMenu();
}
await createMenu();

await dispatchMenuChangeEvent();
}

async function setBrowserAction() {
Expand Down Expand Up @@ -985,8 +1014,13 @@ async function onOptionChange() {
}

async function onStorageChange(changes, area) {
if (changes.setContextMenuEvent?.newValue) {
if (await isStorageReady({area: mv3 ? 'session' : 'local'})) {
if (await isStorageReady({area: mv3 ? 'session' : 'local'})) {
const menuChangeEvent = encodeStorageData('menuChangeEvent', {
name: 'private',
active: browser.extension.inIncognitoContext
});

if (changes[menuChangeEvent]?.newValue) {
await queue.add(createMenu);
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/storage/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
"20230713165504_add_perma.cc",
"20230715152710_add_ghostarchive",
"20230718120215_add_webcite",
"20240514170322_add_appversion"
"20240514170322_add_appversion",
"20240619180111_add_menuchangeevent"
],
"session": ["20240514122825_initial_version"]
"session": [
"20240514122825_initial_version"
]
}
}
17 changes: 17 additions & 0 deletions src/storage/revisions/local/20240619180111_add_menuchangeevent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const message = 'Add menuChangeEvent';

const revision = '20240619180111_add_menuchangeevent';

async function upgrade() {
const changes = {
menuChangeEvent: 0,
privateMenuChangeEvent: 0
};

await browser.storage.local.remove('setContextMenuEvent');

changes.storageVersion = revision;
return browser.storage.local.set(changes);
}

export {message, revision, upgrade};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const revision = '20240514122825_initial_version';
async function upgrade() {
const changes = {
platformInfo: null,
setContextMenuEvent: 0
menuChangeEvent: 0,
privateMenuChangeEvent: 0
};

changes.storageVersion = revision;
Expand Down
56 changes: 30 additions & 26 deletions src/storage/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,43 +50,47 @@ async function ensureStorageReady({area = 'local'} = {}) {
}
}

function encodeStorageData(data, context) {
if (context?.active) {
if (typeof data === 'string') {
return `${context.name}${capitalizeFirstLetter(data)}`;
} else if (Array.isArray(data)) {
const items = [];
function processStorageKey(key, contextName, {encode = true} = {}) {
if (encode) {
return `${contextName}${capitalizeFirstLetter(key)}`;
} else {
return lowercaseFirstLetter(key.replace(new RegExp(`^${contextName}`), ''));
}
}

for (const item of data) {
items.push(`${context.name}${capitalizeFirstLetter(item)}`);
}
function processStorageData(data, contextName, {encode = true} = {}) {
if (typeof data === 'string') {
return processStorageKey(data, contextName, {encode});
} else if (Array.isArray(data)) {
const items = [];

return items;
} else {
const items = {};
for (const item of data) {
items.push(processStorageKey(item, contextName, {encode}));
}

for (const [key, value] of Object.entries(data)) {
items[`${context.name}${capitalizeFirstLetter(key)}`] = value;
}
return items;
} else {
const items = {};

return items;
for (const [key, value] of Object.entries(data)) {
items[processStorageKey(key, contextName, {encode})] = value;
}

return items;
}
}

function encodeStorageData(data, context) {
if (context?.active) {
return processStorageData(data, context.name, {encode: true});
}

return data;
}

function decodeStorageData(data, context) {
if (context?.active) {
const items = {};

for (const [key, value] of Object.entries(data)) {
items[
lowercaseFirstLetter(key.replace(new RegExp(`^${context.name}`), ''))
] = value;
}

return items;
return processStorageData(data, context.name, {encode: false});
}

return data;
Expand Down Expand Up @@ -119,4 +123,4 @@ async function clear({area = 'local'} = {}) {
}

export default {get, set, remove, clear};
export {isStorageArea, isStorageReady};
export {isStorageArea, isStorageReady, encodeStorageData, decodeStorageData};

0 comments on commit d4755b7

Please sign in to comment.