The communication back is again done with an event. You can use a one-shot listener to achieve a promise-based response.
I will be passing an extra request ID so that one-shot listeners don't accidentally get confused; maybe it's an overkill.
Content script:
window.addEventListener("getChromeData", function(evt) { var request = evt.detail; var response = {requestId: request.id}; // do Chrome things with request.data, add stuff to response.data window.dispatchEvent(new CustomEvent("sendChromeData", {detail: response})); }, false);
Injected script:
var ChromeRequest = (function(){ var requestId = 0; function getData(data) { var id = requestId++; return new Promise(function(resolve, reject) { var listener = function(evt) { if(evt.detail.requestId == id) { // Deregister self window.removeEventListener("sendChromeData", listener); resolve(evt.detail.data); } } window.addEventListener("sendChromeData", listener); var payload = { data: data, id: id }; window.dispatchEvent(new CustomEvent("getChromeData", {detail: payload})); }); } return { getData: getData }; })(); ChromeRequest.getData("whatever").then(function(data){/* ... */});
Please note, you have to use detail attribute of a CustomEvent, it's not an arbitrary property.