0

I am trying to create a pretty simple chrome extension that enables me to navigate the browser back and forward using horizontal scrolling. I want to be able to enable and disable the extension; however I want the state to persist when the tab changes (whether the active tab is a new url or a new tab is launched).

Here is my current code that partially works for only the active tab:

manifest.json:

{
  "name": "SwipeNav",
  "action": {},
  "manifest_version": 3,
  "version": "0.1",
  "description": "Navigates browser forward and back with 2 finger swiping",
  "permissions": [
    "activeTab",
    "scripting",
    "tabs",
    "alarms"
  ],
  "background": {
    "service_worker": "background.js"
  }
}

background.js

function twoFingerBack() {
    console.log("Enabling 2 finger scrolling");
    
    let timer;

    window.addEventListener("wheel", function(event) {
        clearTimeout(timer);
        if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
            timer = setTimeout(function() {
                if (event.deltaX > 0) {
                    history.back();
                } else if (event.deltaX < 0) {
                    history.forward();
                }
            }, 200);
        }
    });
}

function removeExt() {
    console.log("Disabling 2 finger scrolling");
    window.removeEventListener("wheel", removeExt)

}

let enabled = true;
chrome.action.onClicked.addListener((tab) => {
    enabled = !enabled;
    if (enabled) {
        if (!tab.url.includes("chrome://")) {
            chrome.scripting.executeScript({
                target: { tabId: tab.id },
                function: twoFingerBack
            });
        }
    } else {
        if (!tab.url.includes("chrome://")) {
            chrome.scripting.executeScript({
                target: { tabId: tab.id },
                function: removeExt
            });
        }
    }
});

I have read that I can use chrome.tabs.onActivated however I could not get this to work at all. Not sure if that is the right path.

Any help is appreciated...thanks!

1 Answers1

1
  1. Save the state in storage. Since localStorage is not available in a service worker you can use chrome.storage.local.
  2. Register/unregister the content script, so it runs automatically next time the tab is loaded.
  3. Process all tabs using executeScript to apply the changes to the currently loaded pages.
  4. Use a global function for the wheel listener and use its name in removeEventListener.

// manifest.json

  • Add "storage" inside "permissions"
  • Add "host_permissions": ["<all_urls>"]

// background.js

chrome.action.onClicked.addListener(async (tab) => {
  let {enabled} = await chrome.storage.local.get('enabled');
  enabled = !enabled;
  chrome.storage.local.set({enabled});
  await chrome.scripting.unregisterContentScripts({ids: ['foo']}).catch(() => {});
  if (enabled) {
    chrome.scripting.registerContentScripts([{
      id: 'foo',
      js: ['content.js'],
      matches: ['<all_urls>'],
      runAt: 'document_start',
    }]);
  }
  const execOpts = enabled ? {files: ['content.js']} : {func: removeExt};
  const tabs = (await chrome.tabs.query({}))
    .sort(t => t.active ? -1 : 0); // processing the active tab(s) first
  for (const {id} of tabs) {
    chrome.scripting.executeScript({target: {tabId: id}, ...execOpts})
      .catch(() => {});
  }
});

function removeExt() {
  console.log('Disabling');
  // onWheel is the name of the global function used in content.js 
  if (typeof onWheel === 'function') removeEventListener('wheel', onWheel);
}

// content.js

console.log('Enabling');
if (typeof onWheel !== 'function') (() => {
  let timer;
  window.onWheel = e => {
    clearTimeout(timer);
    const x = e.deltaX;
    if (x && Math.abs(x) > Math.abs(e.deltaY)) {
      timer = setTimeout(() => x > 0 ? history.back() : history.forward(), 200);
    }
  };
  addEventListener('wheel', onWheel);
})();
wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • As per the docs, [https://developer.chrome.com/docs/extensions/reference/action/] The action.onClicked event will not be dispatched if the extension action has specified a popup to show on click on the current tab. – SkyRar Feb 05 '23 at 18:50
  • I removed the default popup from manifest to try this code out but getting Nonexistent script ID 'foo' – SkyRar Feb 05 '23 at 19:12
  • 1
    1) Your manifest.json is already correct so onClicked will be dispatched. 2) I've fixed the code. – wOxxOm Feb 06 '23 at 06:40
  • This does not remove the eventlistner `onWheel` because it does not store the reference of that function that was added in content.js. Every time the `chrome.scripting.executeScript` executed, it creates a new reference. Also it gives an error `onWheel` not defined – SkyRar Feb 09 '23 at 13:01
  • 1
    Indeed, see the fixed code. – wOxxOm Feb 09 '23 at 15:26
  • This content script is not loaded until you refresh the page when you use `chrome.scripting.registerContentScripts`. Is there any other way? – SkyRar Feb 12 '23 at 08:22
  • Yes, see [Chrome extension content script re-injection after upgrade or install](https://stackoverflow.com/q/10994324) – wOxxOm Feb 12 '23 at 10:34