Disable Extensions Mod
-
What?
Inspired by this extension and this feature request
This mod is not as user-friendly as the previously mentioned extension by @luetage & @sjudenim
But if, as I do, you dislike the idea of having to run a whole separate extension just so you can disable extensions quickly, this is an alternative.Usage
This mod adds the icons for all your extensions into the extension drop-down (below your 'hidden' ones), and you can Ctrl+Click them to disable them.Hovering over an extension icon will tell you its "short name".
IMPORTANT: You MUST have the "Extensions Toggle" setting under "Address Bar" turned OFF.
Also, this relies on the extension drop-down, so you need to have at least one extension with a browser action button enabled, but hidden in order for this to work.
Demo
Installation
As acustom.js
mod:/** * Disable Extensions (a mod for vivaldi) * @author lonm.vivaldi.net */ (function disableExtensions(){ "use strict"; /** * Create an extension button to show in dropdown if it is disabled * @param {*} extensionInfo * @returns DOM button */ function makeCustomExtensionButton(extensionInfo){ const span = document.createElement("span"); const button = document.createElement("button"); button.className = "button-hidden-popup button-toolbar browserAction-button lonm-demod"; button.tabIndex = "-1"; button.id = extensionInfo.id; button.title = extensionInfo.shortName + (extensionInfo.enabled ? "" : " (Disabled)"); button.style.opacity = extensionInfo.enabled ? "1" : "0.5"; button.draggable = false; listenToExtensionButton(button); const img = document.createElement("img") if(extensionInfo.icons && extensionInfo.icons.length > 1){ img.src = extensionInfo.icons[0].url; } else { img.src = "chrome://favicon/chrome://extensions/"; } button.appendChild(img); span.appendChild(button); return span; } /** * Create a separator for the extension dropdown * @param title string * @returns DOM separator */ function createSeparator(title){ const separator = document.createElement("div"); separator.style.border = "1px solid var(--colorBorderIntense)"; separator.style.width = "100%"; separator.style.height = "2px"; separator.title = title; return separator; } /** * Add a listener to a specific button * @param {*} button DOM */ function listenToExtensionButton(button){ button.addEventListener("click", toggleDisabled); } /** * Get all of the extensions that already have an icon displayed * @returns array of strings of extension ids */ function alreadyShownExtensions(){ const buttons = document.querySelectorAll(".button-toolbar.browserAction-button:not(.actionVisibility-hidden)"); const arrr = []; buttons.forEach(button => { arrr.push(button.id); }); return arrr; } /** * The observer fired, so the dropdown was added * so add the "disable" listener to each button * @param {*} mutationList */ function dropdownObserverFired(mutationList){ mutationList.forEach(mutation => { mutation.addedNodes.forEach(dropdown => { if(dropdown.classList.contains("extension-popup")){ dropdown.childNodes.forEach(button => { listenToExtensionButton(button); }); chrome.management.getAll(extensions => { const presentInUi = alreadyShownExtensions(); const missingFromUi = extensions.filter(x => presentInUi.indexOf(x.id)===-1); const missingFromUiAndEnabled = missingFromUi.filter(x => x.enabled); const missingFromUiAndDisabled = missingFromUi.filter(x => !x.enabled); dropdown.appendChild(createSeparator("βΌ Extensions without a Browser Action")); missingFromUiAndEnabled.forEach(extension => { const newButton = makeCustomExtensionButton(extension); dropdown.appendChild(newButton); }); dropdown.appendChild(createSeparator("βΌ Disabled Extensions")); missingFromUiAndDisabled.forEach(extension => { const newButton = makeCustomExtensionButton(extension); dropdown.appendChild(newButton); }); }); } }); }); } /** * The observer fired, so a new extension button was added * add the "disable" listener to it * @remark new addition may be the browser action popup * which is a DIV so test to make sure it is a SPAN * @param {*} mutationList */ function buttonObserverFired(mutationList){ mutationList.forEach(mutation => { mutation.addedNodes.forEach(node => { if(node.tagName.toUpperCase()==="SPAN"){ listenToExtensionButton(node.querySelector("button")); } }); }); } /** Observers */ const dropdownObserver = new MutationObserver(dropdownObserverFired); const buttonObserver = new MutationObserver(buttonObserverFired); const observerConfig = {childList: true}; /** * Listen to existing buttons */ function listenToExistingButtons(){ const existingButtons = document.querySelectorAll(".toolbar-addressbar > .extensions-wrapper > span > .browserAction-button"); existingButtons.forEach(listenToExtensionButton); } /** * Init the mod */ function init(){ if(!document.querySelector(".extensions-wrapper")){ setTimeout(init, 500); } else { dropdownObserver.observe(document.querySelector(".toolbar-addressbar > .extensions-wrapper > .button-group"), observerConfig); buttonObserver.observe(document.querySelector(".toolbar-addressbar > .extensions-wrapper"), observerConfig); listenToExistingButtons(); } } /** * User clicked on extension button * If it meets our criteria, toggle the disableness * @param {*} event */ function toggleDisabled(event){ if(event.ctrlKey){ let target = event.target; if(event.target.tagName.toUpperCase()==="IMG"){ target = target.parentElement; } const extensionContainer = target.parentElement.parentElement; chrome.management.get(target.id, extensionInfo => { doToggledisabled(extensionInfo, extensionContainer, target); }); event.preventDefault(); } } /** * We will now toggle the state of this item * @param {*} extensionInfo * @param {*} extensionContainer DOM * @param {*} extensionButton DOM */ function doToggledisabled(extensionInfo, extensionContainer, extensionButton){ if(!extensionInfo.mayDisable){ return; } const newEnabledstate = !extensionInfo.enabled; chrome.management.setEnabled(extensionInfo.id, newEnabledstate, () => { extensionInfo.enabled = newEnabledstate; toggleCallback(extensionInfo, extensionContainer, extensionButton); }); } /** * Extension was toggled. * Clean upp the UI afterwards * @param {*} extensionInfo * @param {*} extensionContainer DOM * @param {*} extensionButton DOM */ function toggleCallback(extensionInfo, extensionContainer, extensionButton){ listenToExistingButtons(); const containerIsPopup = extensionContainer.classList.contains("extension-popup"); if(!containerIsPopup){ return; } const isCustomButton = extensionButton.classList.contains("lonm-demod"); if(extensionInfo.enabled){ extensionContainer.removeChild(extensionButton.parentElement); const buttonAdddToMainExtensionsArea = alreadyShownExtensions().indexOf(extensionInfo.id) > -1; if(isCustomButton && !buttonAdddToMainExtensionsArea){ const newButton = makeCustomExtensionButton(extensionInfo); const disabledSeparator = extensionContainer.querySelector("div:nth-of-type(2)"); extensionContainer.insertBefore(newButton, disabledSeparator); } } else { const newButton = makeCustomExtensionButton(extensionInfo); extensionContainer.appendChild(newButton); if(isCustomButton){ extensionContainer.removeChild(extensionButton.parentElement); } } } setTimeout(init, 500); })();
Known Issues
- After re-enabling an extension that was set to 'hidden', it will show up in the disabled section. This will only happen until you close and re-open the extension drop-down.
- I can't get the context menu to appear on the mod-added extension icons
Changelog
- Initial Release
-
Hm, tested with this as the only mod, doesn't work for meβno extension drop-down shown. (Extensions Toggle is unchecked in Settings and my other mods work fine, so it's not a problem with my mod setup either.) Console says
Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'. at init (custom.js:139)
. Running Vivaldi 2.0.1309.37 Stable on Windows 10, if that's relevant. -
@valiowk Unfortunately, the way I've build this mod up relies on the fact that I have at least one of my mods always enabled, and always hidden. (In my case, that will be HTTPS Everywhere).
If you don't have any mods enabled and hidden, then the extension drop-down won't show, and that means the extension can't activate.
I'll add that as a clarification in the original post.
-
@lonm I see! I was going to test whether it was compatible with luetage's improved extension toggle, and if it wasn't, ask whether it was possible to make it compatible with that, but I guess the answer is probably no.
-
@valiowk It's certainly compatible with the extension. But if you're using the extension, there's not much point in using the mod, they both do similar things.
-
@lonm I wasn't referring to the extension, I was referring to luetage's mod.
-
@valiowk Oh, apologies I misread that. I don't think this would be compatible was their mod.
You could certainly use it to ctrl+click an item to disable it, but then there would be no way to re-enable it.
I could consider re-writing the mod to use a custom drop-down, which might allow for better compatibility (as well as if you don't use the drop-down normally).
-
@lonm It would be greatly appreciated if you could consider it!
Also, is there any possibility of using this mod to also toggle/activate other options hidden in "Details" such as "Allow in incognito" or "Open extension website"? It's so tedious to toggle them for multiple extensions on the Extensions page now that they're all hidden behind an extra layer, and no extension can touch "Allow in incognito" toggability, so the only hope lies in a mod or the developers...
-
@valiowk Unfortunately, I am limited by what the
chrome.management
api offers.From the "Methods" list here: https://developer.chrome.com/extensions/management The only things I can really do are:
- Get a list of extensions
- Turn them on or off
- Uninstall them
It would be nice if there was a kind of "matrix" style dashboard for extensions where you could see everything in one screen, but alas, I don't think that would be possible in a mod.
-