diff --git a/background_scripts/main.js b/background_scripts/main.js index 7d37e6b1b..d0988172b 100644 --- a/background_scripts/main.js +++ b/background_scripts/main.js @@ -597,6 +597,69 @@ const HintCoordinator = { }, }; +let globallyDisabled = false; +const globallyDisabledLoaded = chrome.storage.local.get("globallyDisabled").then((items) => { + if (items.globallyDisabled != null) globallyDisabled = items.globallyDisabled; +}); + +function getIconSet() { + if (bgUtils.isFirefox()) { + return { + "enabled": "../icons/action_enabled.svg", + "partial": "../icons/action_partial.svg", + "disabled": "../icons/action_disabled.svg", + }; + } + return { + "enabled": { + "16": "../icons/action_enabled_16.png", + "32": "../icons/action_enabled_32.png", + }, + "partial": { + "16": "../icons/action_partial_16.png", + "32": "../icons/action_partial_32.png", + }, + "disabled": { + "16": "../icons/action_disabled_16.png", + "32": "../icons/action_disabled_32.png", + }, + }; +} + +async function toggleGloballyDisabled() { + globallyDisabled = !globallyDisabled; + chrome.storage.local.set({ globallyDisabled }); + const iconSet = getIconSet(); + const whichIcon = globallyDisabled ? "disabled" : "enabled"; + const tabs = await chrome.tabs.query({}); + for (const tab of tabs) { + chrome.action.setIcon({ path: iconSet[whichIcon], tabId: tab.id }).catch(() => {}); + chrome.tabs.sendMessage(tab.id, { + handler: "toggleGloballyDisabled", + disabled: globallyDisabled, + }).catch(() => {}); + } +} + +chrome.commands.onCommand.addListener(async (command) => { + if (command === "toggleGloballyDisabled") { + await toggleGloballyDisabled(); + } +}); + +chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => { + if (request.handler === "getGloballyDisabled") { + chrome.storage.local.get("globallyDisabled", (items) => { + sendResponse({ globallyDisabled: items.globallyDisabled ?? false }); + }); + return true; + } else if (request.handler === "toggleGloballyDisabled") { + toggleGloballyDisabled().then(() => sendResponse()); + return true; + } + return false; +}); + const sendRequestHandlers = { runBackgroundCommand(request, sender) { return BackgroundCommands[request.registryEntry.command](request, sender); @@ -661,8 +724,14 @@ const sendRequestHandlers = { async initializeFrame(request, sender) { // Check whether the extension is enabled for the top frame's URL, rather than the URL of the // specific frame that sent this request. + await globallyDisabledLoaded; const enabledState = exclusions.isEnabledForUrl(sender.tab.url); + if (globallyDisabled) { + enabledState.isEnabledForUrl = false; + enabledState.passKeys = ""; + } + const isTopFrame = sender.frameId == 0; if (isTopFrame) { let whichIcon; @@ -674,30 +743,7 @@ const sendRequestHandlers = { whichIcon = "enabled"; } - let iconSet = { - "enabled": { - "16": "../icons/action_enabled_16.png", - "32": "../icons/action_enabled_32.png", - }, - "partial": { - "16": "../icons/action_partial_16.png", - "32": "../icons/action_partial_32.png", - }, - "disabled": { - "16": "../icons/action_disabled_16.png", - "32": "../icons/action_disabled_32.png", - }, - }; - - if (bgUtils.isFirefox()) { - // Only Firefox supports SVG icons. - iconSet = { - "enabled": "../icons/action_enabled.svg", - "partial": "../icons/action_partial.svg", - "disabled": "../icons/action_disabled.svg", - }; - } - + const iconSet = getIconSet(); chrome.action.setIcon({ path: iconSet[whichIcon], tabId: sender.tab.id }); } @@ -926,6 +972,17 @@ Object.assign(globalThis, { BackgroundCommands, majorVersionHasIncreased, nextZoomLevel, + toggleGloballyDisabled, + sendRequestHandlers, +}); + +Object.defineProperty(globalThis, "globallyDisabled", { + get() { + return globallyDisabled; + }, + set(v) { + globallyDisabled = v; + }, }); // The chrome.runtime.onStartup and onInstalled events are not fired when disabling and then diff --git a/content_scripts/vimium_frontend.js b/content_scripts/vimium_frontend.js index c4d5331ec..36e1ded8f 100644 --- a/content_scripts/vimium_frontend.js +++ b/content_scripts/vimium_frontend.js @@ -352,6 +352,15 @@ globalThis.lastFocusedInput = (function () { })(); const messageHandlers = { + toggleGloballyDisabled(request) { + if (request.disabled) { + isEnabledForUrl = false; + HUD.show("Vimium disabled", 2000); + } else { + checkIfEnabledForUrl(); + HUD.show("Vimium enabled", 2000); + } + }, getFocusStatus(_request, _sender) { return { focused: windowIsFocused(), @@ -401,7 +410,9 @@ async function handleMessage(request, sender) { // Some request are handled elsewhere in the code base; ignore them here. const shouldHandleMessage = request.handler !== "userIsInteractingWithThePage" && (isEnabledForUrl || - ["checkEnabledAfterURLChange", "runInTopFrame"].includes(request.handler)); + ["checkEnabledAfterURLChange", "runInTopFrame", "toggleGloballyDisabled"].includes( + request.handler, + )); if (shouldHandleMessage) { const result = await messageHandlers[request.handler](request, sender); return result; diff --git a/manifest.json b/manifest.json index 783852cbb..6c383ad8a 100644 --- a/manifest.json +++ b/manifest.json @@ -94,6 +94,11 @@ // "strict_min_version": "109.0" // } // }, + "commands": { + "toggleGloballyDisabled": { + "description": "Toggle Vimium on/off" + } + }, "action": { "default_icon": { "16": "icons/action_disabled_16.png", diff --git a/pages/action.css b/pages/action.css index 145c3bb1d..370a93955 100644 --- a/pages/action.css +++ b/pages/action.css @@ -26,15 +26,21 @@ h1 { padding: var(--padding); } -#dialog-body > * { +#globally-disabled-notice { + padding: var(--padding); + padding-right: 0; + flex-grow: 1; +} + +#dialog-body > *, #globally-disabled-notice > * { margin: 10px 0; } -#dialog-body > *:first-child { +#dialog-body > *:first-child, #globally-disabled-notice > *:first-child { margin-top: 0; } -#dialog-body > *:last-child { +#dialog-body > *:last-child, #globally-disabled-notice > *:last-child { margin-bottom: 0; } diff --git a/pages/action.html b/pages/action.html index 2e1fae6cf..c15cf6a42 100644 --- a/pages/action.html +++ b/pages/action.html @@ -46,6 +46,11 @@