Call background function of Chrome extension from a site
Before a web page is able to call a background page's function, the following problems need to be solved:
- Be able to use
hello();
from a web page. This is done by injecting a script defininghello
using Content scripts. The injected function communicates with the content script using a custom event orpostMessage
. - 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: - Send a reply from the background page (
sendMessage
/onMessage
, see below). - In the content script, create a custom event or use
postMessage
to send a message to the web page. - 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.
There is a builtin solution to Send messages from web pages to the extension
mainfest.json
"externally_connectable": { "matches": ["*://*.example.com/*"]}
Web page:
// The ID of the extension we want to talk to.var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";// Make a simple request:chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url}, function(response) { if (!response.success) handleError(url); });
Extension's background script:
chrome.runtime.onMessageExternal.addListener( function(request, sender, sendResponse) { if (sender.url == blacklistedWebsite) return; // don't allow this web page access if (request.openUrlInEditor) openUrl(request.openUrlInEditor); });
No, with your above code because of background page(s) architecture
Yes with content scripts
Demonstration Using Content Scripts
manifest.json
Registering content scripts myscripts.js
{"name": "NFC","description": "NFC Liken","version": "0.1","manifest_version": 2,"permissions": ["tabs", "http://*/", "https://*/"],"content_scripts": { "matches": "http://www.example.com/*", "js": [ "myscript.js"] },"browser_action": {"default_icon": "sync-icon.png","default_title": "I Like I Tag"}}
Let me know if you need more information.