1

I want to write an extension that does the following:

  • Defines a custom function
  • Allows Javascript code loaded from the Internet to run such a function

The function should take as a parameter an event listener. Basically, something like:

newApiFunctionDefinedInExtension( function( responseHeaders ){
  console.log("Headers arrived!", responseHeaders );
} ; 

Then using chrome.webRequest, my extension (which made newApiFunctionDefinedInExtension available in the first place) will call the listener (in the locally loaded page) every time response headers are received from the network.

I am new to Chrome extensions and cannot find a way to make that happen. It would be great to know:

  • How to make a function defined in a module available to the loaded page's scope
  • How to make such an EventEmitter -- is there a constructor class I can extend?

My goal is simple: the loaded page should define a function, and that function should be called every time there is a network connection.

Merc
  • 16,277
  • 18
  • 79
  • 122
  • The chrome.webRequest docs indicate there is an onHeadersReceived event that you can listen for. Is this not what you are asking for? – adrichman Apr 13 '14 at 23:32
  • Please read my question carefully. I want a function available to the *general javascript world* – Merc Apr 13 '14 at 23:51
  • What is the __general javascript world__? it's not even a google-able phrase. Could you perhaps provide the real-world use case you are describing? Many reads of your question have not provided clarity. – adrichman Apr 14 '14 at 00:21
  • Do you want `somepage.html` to call `newApiFunc`? I'm pretty sure "isolated worlds" will prevent that (and why would `somepage` call the function in the first place?). But you should be able to make `newApiFunc` listen for a [custom event](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events). – Teepeemm Apr 14 '14 at 00:46
  • @adrichman Further clarified the question. – Merc Apr 14 '14 at 00:57
  • @teepeemm THe problem is that the event is triggered by the Google API, and not vice-versa... – Merc Apr 14 '14 at 01:04

1 Answers1

2

Every webRequest event receives information about a request, including the ID of the originating tab.

So, assuming that the tab exists note 1, you can use the following flow:

// background.js
chrome.webRequest.onHeadersReceived.addListener(function(details) {
    if (details.tabId == -1)
        return; // Not related to any tab
    chrome.tabs.sendMessage(details.tabId, {
        responseHeaders: details.responseHeaders
    });
}, {
    urls: ['*://*/*'], // e.g. all http(s) URLs. See match patterns docs
    // types: ['image'] // for example, defaults to **all** request types
}, ['responseHeaders']);

Then, in a content script (declared in the manifest file), you take the message and pass it to the web page:

// contentscript.js
chrome.runtime.onMessage.addListener(function(message) {
    // Assuming that all messages from the background are meant for the page:
    document.dispatchEvent(new CustomEvent('my-extension-event', {
        detail: message
    }));
});

After doing that, your web page can just receive these events as follows:

document.addEventListener('my-extension-event', function(event) {
    var message = event.detail;
    if (message.responseHeaders) {
        // Do something with response headers
    }
});

If you want to put an abstraction on top (e.g. implementing a custom EventEmitter), then you need to inject a script in the main execution environment, and declare your custom API over there.

note 1. For simplicity, I assumed that the tab existed. In reality, that is never true for type "main_frame" (and "sub_frame"), because the page has not yet been rendered. If you want to get response headers for the top-level/frame documents, then you need to temporarily store the response headers in some data structure (e.g. a queue / dictionary) in the background page, and send the data to the content script whenever the script is ready.
This can be implemented by using chrome.runtime.sendMessage in the content script to send a message to the background page. Then, whenever a page has loaded and the content script is ready, the background page can use sendResponse to deliver any queued messages.

Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678