Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call background function of Chrome extension from a site

I am looking for a function inside a webpage te activate a chrome extension.

Imagine that http://www.example.com/test.html contains:

<script>
hello();
</script>

And my background page contains the definition of the hello function:

function hello() {
    alert("test");
}

How can I make sure that the Chrome extension's background page's hello is called when test.html calls hello();?

like image 733
Wouter van Reeven Avatar asked Dec 08 '12 13:12

Wouter van Reeven


People also ask

What is the use of background JS in Chrome extension?

Background script can react to browser events and perform certain actions, the background page is loaded when it is needed, and unloaded when it goes idle. To use the background file, declare it as follows in the manifest.

Do extensions run in the background?

1 Answer. Show activity on this post. A background page runs at all times when the extension is enabled.

How do I send a message extension to a website?

According to the official docs you should use postMessage in the sender and message event listener in the receiver. var data = { type: "FROM_PAGE", text: "Hello from the webpage!" }; window. postMessage(data, "*"); Content script: (injected using chrome.


1 Answers

Before a web page is able to call a background page's function, the following problems need to be solved:

  1. Be able to use hello(); from a web page. This is done by injecting a script defining hello using Content scripts. The injected function communicates with the content script using a custom event or postMessage.
  2. The content script needs to communicate with the background. This is implemented through chrome.runtime.sendMessage.
    If the web page needs to receive a reply as well:
  3. Send a reply from the background page (sendMessage / onMessage, see below).
  4. In the content script, create a custom event or use postMessage to send a message to the web page.
  5. In the web page, handle this message.

All of these methods are asynchronous, and have to be implemented through callback functions.

These steps need to be designed carefully. Here's a generic implementation which implements all of the above steps. What you need to know about the implementation:

  • In the code-to-be-injected, use the sendMessage method whenever the content script need to be contacted.
    Usage: sendMessage(<mixed message> [, <function callback>])

contentscript.js

// Random unique name, to be used to minimize conflicts:
var EVENT_FROM_PAGE = '__rw_chrome_ext_' + new Date().getTime();
var EVENT_REPLY = '__rw_chrome_ext_reply_' + new Date().getTime();

var s = document.createElement('script');
s.textContent = '(' + function(send_event_name, reply_event_name) {
    // NOTE: This function is serialized and runs in the page's context
    // Begin of the page's functionality
    window.hello = function(string) {
        sendMessage({
            type: 'sayhello',
            data: string
        }, function(response) {
            alert('Background said: ' + response);
        });
    };

    // End of your logic, begin of messaging implementation:
    function sendMessage(message, callback) {
        var transporter = document.createElement('dummy');
        // Handles reply:
        transporter.addEventListener(reply_event_name, function(event) {
            var result = this.getAttribute('result');
            if (this.parentNode) this.parentNode.removeChild(this);
            // After having cleaned up, send callback if needed:
            if (typeof callback == 'function') {
                result = JSON.parse(result);
                callback(result);
            }
        });
        // Functionality to notify content script
        var event = document.createEvent('Events');
        event.initEvent(send_event_name, true, false);
        transporter.setAttribute('data', JSON.stringify(message));
        (document.body||document.documentElement).appendChild(transporter);
        transporter.dispatchEvent(event);
    }
} + ')(' + JSON.stringify(/*string*/EVENT_FROM_PAGE) + ', ' +
           JSON.stringify(/*string*/EVENT_REPLY) + ');';
document.documentElement.appendChild(s);
s.parentNode.removeChild(s);


// Handle messages from/to page:
document.addEventListener(EVENT_FROM_PAGE, function(e) {
    var transporter = e.target;
    if (transporter) {
        var request = JSON.parse(transporter.getAttribute('data'));
        // Example of handling: Send message to background and await reply
        chrome.runtime.sendMessage({
            type: 'page',
            request: request
        }, function(data) {
            // Received message from background, pass to page
            var event = document.createEvent('Events');
            event.initEvent(EVENT_REPLY, false, false);
            transporter.setAttribute('result', JSON.stringify(data));
            transporter.dispatchEvent(event);
        });
    }
});

background.js

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    if (message && message.type == 'page') {
        var page_message = message.message;
        // Simple example: Get data from extension's local storage
        var result = localStorage.getItem('whatever');
        // Reply result to content script
        sendResponse(result);
    }
});

A Chrome extension is not complete without a manifest file, so here's the manifest.json file which I used to test the answer:

{
    "name": "Page to background and back again",
    "version": "1",
    "manifest_version": 2,
    "background": {
        "scripts": ["background.js"]
    },
    "content_scripts": [{
        "matches": ["http://jsfiddle.net/jRaPj/show/*"],
        "js": ["contentscript.js"],
        "all_frames": true,
        "run_at": "document_start"
    }]
}

This extension was tested at http://jsfiddle.net/jRaPj/show/ (containing hello(); as seen in the question), and shows a dialog saying "Background said: null".
Open the background page, use localStorage.setItem('whatever', 'Hello!'); to see that the message is correctly changed.

like image 60
Rob W Avatar answered Sep 29 '22 01:09

Rob W