Search code examples
javascriptgoogle-chrome-extension

Send message to background.js from options.js in Chrome Extension


I can find plenty of questions for sending a message from Bbackground to content scripts or from popup to background scripts, but I can't find any on how to send a message from options.js (triggered from my options page) to background.js for my Chrome extension

manifest.js

{
  ...
  "manifest_version": 3,
  "permissions": [
    "alarms",
    "contextMenus",
    "notifications",
    "storage"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  },
  "options_page": "options.html",
  "icons": {
    "48": "/assets/icons/icon-48.png",
    "128": "/assets/icons/icon-128.png"
  }
}

Code in options.js

// Save options
document.querySelector('#save').addEventListener('click', ()=> {
  // Check for selected regions
  let selectedRegions = [];
  regionChecks.forEach(elm => {
    if (elm.checked)
      selectedRegions.push(elm.id.replace('region-', ''))
  });
  // Save selections
  chrome.storage.sync.set({ 'regions': selectedRegions });

  // Check for refresh period
  let refreshPeriod = document.querySelector('#refresh');
  if (refreshPeriod) {
    refreshPeriod = parseInt(refreshPeriod.value);
    if (refreshPeriod === NaN) {
      refreshPeriod = 5;
    } else if(refreshPeriod < 2) {
      refreshPeriod = 2;
    } else if (refreshPeriod > 120) {
      refreshPeriod = 120;
    }
    // Save period
    chrome.storage.sync.set({ 'refresh': refreshPeriod });
  }

  // Loop through features and remove if region gone
  chrome.storage.local.get(['features'], featuresData => {
    let featureCount = 0;
    if (featuresData.features) {
      featureSet = featuresData.features;
      Object.keys(featureSet).forEach(key => {
        if (!selectedRegions.includes(featureSet[key].Region)) {
          delete featureSet[key];
        } else if (featureSet[key].Status !== "read") {
          featureCount = featureCount + 1;
        }
      });
      // Update features data
      chrome.storage.local.set({ 'features': featureSet });
      // Update the badge
      if (featureCount > 0) {
        chrome.action.setBadgeText({ text: featureCount.toString() });
      } else {
        chrome.action.setBadgeText({ text: '' });
      }
    }
  });

  // Trigger an update
  chrome.runtime.sendMessage('', {
    type: 'update'
  });

  // Close the options page
  close();
});

Code in background.js

// Listen for messages
chrome.runtime.onMessage.addListener(data => {
  console.log(data);
  if (data.type === 'update') {
    scheduleRequest();
  }
});

But nothing is happening

I've tried using tabs.sendMessage, but even if I put tabs in permissions it tells me that it isn't defined when I add it to the function chrome.tabs.sendMessage(tabs[0].id, {...


Solution

  • After some input from @woxxom and @Norio, and a little bit more troubleshooting, this is how I managed to get it to work

    option.js:

    // Trigger an update
    chrome.runtime.sendMessage({ type: 'update' }, result => {
      // Close the options page
      close();
    });
    

    I had to move the close(); event into the return function so it didn't prematurely close off the code. I also had to have a paramter in the return function for some reason otherwise it wouldn't run with an empty arrow function.

    background.js:

    // Listen for messages
    chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
      if (message.type === 'update') {
        startRequest(true).then(sendResponse);
        return true;
      }
    });
    

    My function to be triggered in the background.js file was already an async promise function, so I had to add a .then() for the onMessage response function to trigger once it had run. It also wouldn't work without having return true; in there.