diff --git a/src/background/main.js b/src/background/main.js index f7c1e34..26b1c1d 100644 --- a/src/background/main.js +++ b/src/background/main.js @@ -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, @@ -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', @@ -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 = '', @@ -178,6 +209,10 @@ async function getMenuItem({ type }; + if (browser.extension.inIncognitoContext) { + params.id += '_private'; + } + if (documentUrlPatterns) { params.documentUrlPatterns = documentUrlPatterns; } @@ -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() { @@ -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); } } diff --git a/src/storage/config.json b/src/storage/config.json index b8b7439..6860324 100755 --- a/src/storage/config.json +++ b/src/storage/config.json @@ -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" + ] } } diff --git a/src/storage/revisions/local/20240619180111_add_menuchangeevent.js b/src/storage/revisions/local/20240619180111_add_menuchangeevent.js new file mode 100644 index 0000000..72e23b1 --- /dev/null +++ b/src/storage/revisions/local/20240619180111_add_menuchangeevent.js @@ -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}; diff --git a/src/storage/revisions/session/20240514122825_initial_version.js b/src/storage/revisions/session/20240514122825_initial_version.js index 638997f..96ddb3b 100644 --- a/src/storage/revisions/session/20240514122825_initial_version.js +++ b/src/storage/revisions/session/20240514122825_initial_version.js @@ -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; diff --git a/src/storage/storage.js b/src/storage/storage.js index b218be6..af65165 100755 --- a/src/storage/storage.js +++ b/src/storage/storage.js @@ -50,27 +50,39 @@ 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; @@ -78,15 +90,7 @@ function encodeStorageData(data, context) { 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; @@ -119,4 +123,4 @@ async function clear({area = 'local'} = {}) { } export default {get, set, remove, clear}; -export {isStorageArea, isStorageReady}; +export {isStorageArea, isStorageReady, encodeStorageData, decodeStorageData};