Skip to content

Commit d4755b7

Browse files
committed
fix: recover from error during context menu setup
1 parent 6c662c7 commit d4755b7

File tree

5 files changed

+102
-43
lines changed

5 files changed

+102
-43
lines changed

src/background/main.js

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {v4 as uuidv4} from 'uuid';
22
import Queue from 'p-queue';
33

44
import {initStorage} from 'storage/init';
5-
import {isStorageReady} from 'storage/storage';
5+
import {isStorageReady, encodeStorageData} from 'storage/storage';
66
import storage from 'storage/storage';
77
import {
88
getEnabledEngines,
@@ -131,6 +131,11 @@ async function removeMenuItem(menuItemId, {throwError = false} = {}) {
131131
}
132132
}
133133

134+
async function removeAllMenuItems() {
135+
// removes context menu items from all instances
136+
await browser.contextMenus.removeAll();
137+
}
138+
134139
async function createMenu() {
135140
const context = {
136141
name: 'private',
@@ -153,13 +158,39 @@ async function createMenu() {
153158
await createMenuItem(item);
154159
}
155160
} catch (err) {
156-
// removes context menu items from all instances
157-
await browser.contextMenus.removeAll();
161+
// The storage may have been cleared, and the context menu state is
162+
// no longer known. All menu items are removed and the menu is recreated.
163+
164+
await removeAllMenuItems();
165+
166+
for (const item of newItems) {
167+
await createMenuItem(item);
168+
}
169+
170+
if (runOnce('createMenuError')) {
171+
await dispatchMenuChangeEvent();
172+
}
158173

159174
throw err;
160175
}
161176
}
162177

178+
async function dispatchMenuChangeEvent() {
179+
if (['chrome', 'edge', 'opera'].includes(targetEnv)) {
180+
// notify the other background script instance
181+
await storage.set(
182+
{menuChangeEvent: Date.now()},
183+
{
184+
area: mv3 ? 'session' : 'local',
185+
context: {
186+
name: 'private',
187+
active: !browser.extension.inIncognitoContext
188+
}
189+
}
190+
);
191+
}
192+
}
193+
163194
async function getMenuItem({
164195
id,
165196
title = '',
@@ -178,6 +209,10 @@ async function getMenuItem({
178209
type
179210
};
180211

212+
if (browser.extension.inIncognitoContext) {
213+
params.id += '_private';
214+
}
215+
181216
if (documentUrlPatterns) {
182217
params.documentUrlPatterns = documentUrlPatterns;
183218
}
@@ -734,15 +769,9 @@ async function onActionPopupClick(engine, docUrl) {
734769
}
735770

736771
async function setContextMenu() {
737-
if (['chrome', 'edge', 'opera'].includes(targetEnv)) {
738-
// notify all background script instances
739-
await storage.set(
740-
{setContextMenuEvent: Date.now()},
741-
{area: mv3 ? 'session' : 'local'}
742-
);
743-
} else {
744-
await createMenu();
745-
}
772+
await createMenu();
773+
774+
await dispatchMenuChangeEvent();
746775
}
747776

748777
async function setBrowserAction() {
@@ -985,8 +1014,13 @@ async function onOptionChange() {
9851014
}
9861015

9871016
async function onStorageChange(changes, area) {
988-
if (changes.setContextMenuEvent?.newValue) {
989-
if (await isStorageReady({area: mv3 ? 'session' : 'local'})) {
1017+
if (await isStorageReady({area: mv3 ? 'session' : 'local'})) {
1018+
const menuChangeEvent = encodeStorageData('menuChangeEvent', {
1019+
name: 'private',
1020+
active: browser.extension.inIncognitoContext
1021+
});
1022+
1023+
if (changes[menuChangeEvent]?.newValue) {
9901024
await queue.add(createMenu);
9911025
}
9921026
}

src/storage/config.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
"20230713165504_add_perma.cc",
1717
"20230715152710_add_ghostarchive",
1818
"20230718120215_add_webcite",
19-
"20240514170322_add_appversion"
19+
"20240514170322_add_appversion",
20+
"20240619180111_add_menuchangeevent"
2021
],
21-
"session": ["20240514122825_initial_version"]
22+
"session": [
23+
"20240514122825_initial_version"
24+
]
2225
}
2326
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const message = 'Add menuChangeEvent';
2+
3+
const revision = '20240619180111_add_menuchangeevent';
4+
5+
async function upgrade() {
6+
const changes = {
7+
menuChangeEvent: 0,
8+
privateMenuChangeEvent: 0
9+
};
10+
11+
await browser.storage.local.remove('setContextMenuEvent');
12+
13+
changes.storageVersion = revision;
14+
return browser.storage.local.set(changes);
15+
}
16+
17+
export {message, revision, upgrade};

src/storage/revisions/session/20240514122825_initial_version.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ const revision = '20240514122825_initial_version';
55
async function upgrade() {
66
const changes = {
77
platformInfo: null,
8-
setContextMenuEvent: 0
8+
menuChangeEvent: 0,
9+
privateMenuChangeEvent: 0
910
};
1011

1112
changes.storageVersion = revision;

src/storage/storage.js

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,43 +50,47 @@ async function ensureStorageReady({area = 'local'} = {}) {
5050
}
5151
}
5252

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

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

64-
return items;
65-
} else {
66-
const items = {};
67+
for (const item of data) {
68+
items.push(processStorageKey(item, contextName, {encode}));
69+
}
6770

68-
for (const [key, value] of Object.entries(data)) {
69-
items[`${context.name}${capitalizeFirstLetter(key)}`] = value;
70-
}
71+
return items;
72+
} else {
73+
const items = {};
7174

72-
return items;
75+
for (const [key, value] of Object.entries(data)) {
76+
items[processStorageKey(key, contextName, {encode})] = value;
7377
}
78+
79+
return items;
80+
}
81+
}
82+
83+
function encodeStorageData(data, context) {
84+
if (context?.active) {
85+
return processStorageData(data, context.name, {encode: true});
7486
}
7587

7688
return data;
7789
}
7890

7991
function decodeStorageData(data, context) {
8092
if (context?.active) {
81-
const items = {};
82-
83-
for (const [key, value] of Object.entries(data)) {
84-
items[
85-
lowercaseFirstLetter(key.replace(new RegExp(`^${context.name}`), ''))
86-
] = value;
87-
}
88-
89-
return items;
93+
return processStorageData(data, context.name, {encode: false});
9094
}
9195

9296
return data;
@@ -119,4 +123,4 @@ async function clear({area = 'local'} = {}) {
119123
}
120124

121125
export default {get, set, remove, clear};
122-
export {isStorageArea, isStorageReady};
126+
export {isStorageArea, isStorageReady, encodeStorageData, decodeStorageData};

0 commit comments

Comments
 (0)