Dave
Dave

Reputation: 2601

Cannot create notification in Chrome Extension. Receive Uncaught TypeError: Cannot read property 'create' of undefined error

I am trying to display a notification in my Chrome extension when an HTTP response is received. Every time I try, I get the following error.

Uncaught TypeError: Cannot read property 'create' of undefined

I have made sure that I have the notifications permission set in the manifest.json.

I thought I might be able to do the notification in a callback function, however anything I pass in for a callback (function, constant, variable, etc) is always undefined.

Here is my relevant code.

function push(info,tab) {
   function modifyDOM(tab_info, callback) {
    var endpoint = "https://blahblahblah";
    var xmlhttp = new XMLHttpRequest();

    alert(callback); //always undefined. 
  
    xmlhttp.onreadystatechange = function() {
      if (this.readyState == 4) {
          if (this.status == 200) { 
            alert(this.responseText); 

             var msg = 'Pushed ' + tab_info.tab.url + ' to endpoint'; 
             var opt = {
                 iconUrl: "images/img48.png",
                 type: 'basic',
                 title: 'Handler',
                 message: msg
               };
               chrome.notifications.create(opt); //Error occurs here. 
          }
          else { 
            alert(callback); //always undefined 
          }
      }
    };
  
    xmlhttp.open("POST", endpoint);
    xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
    xmlhttp.send(JSON.stringify({ "body": {"content": document.body.innerText }, "query": { "url": tab_info.tab.url, "title": tab_info.tab.title} }));
}

chrome.tabs.executeScript({
    code: '(' + modifyDOM + ')(' + JSON.stringify({ "tab" : tab, "callback": 1}) +');' //callback parameter is always undefined in the modifyDOM() function above 
}, (results) => {
    console.log(results[0]);
  });
}

manifest.json

{
 "manifest_version": 2,    
 "version": "1.0",               
 "name": "Handler",   
 "description": "Push to endpoint",
  "icons": {
    "16": "images/img16.png",
    "48": "images/img48.png",
    "128": "images/img128.png"
   },
"permissions": [
    "tabs",
    "contextMenus",
    "*://*/*",
    "notifications"
],
 "background": { 
   "scripts": ["script.js"]
   }
  }

Upvotes: 0

Views: 627

Answers (1)

woxxom
woxxom

Reputation: 73526

Problem 1

The function modifyDOM doesn't run inside the background script. It's only declared there and you convert its code into a string and inject via chrome.tabs.executeScript, which runs the code as a content script in the active tab. Content scripts can't use most of chrome API, they can use only the few basic ones like chrome.i18n, chrome.runtime, chrome.storage.

The solution is to call chrome.notifications in the background script context e.g. send a message from the injected code.

Problem 2

The parameters are passed as a single object { "tab" : tab, "callback": 1} but the function is declared to take two.

The simplest solution is to declare the function to take a single object too and use destructuring.

Result

function modifyDOM({tab, callback}) {
  // ..............
  xmlhttp.onload = function () {
    if (this.status == 200) {
      chrome.runtime.sendMessage('Pushed ' + tab_info.tab.url + ' to endpoint');
    }
  };
  // ..............
}

background script global code:

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  var opt = {
    iconUrl: 'images/img48.png',
    type: 'basic',
    title: 'Handler',
    message,
  };
  chrome.notifications.create(opt); 
});

Upvotes: 2

Related Questions