13

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();?

Rob W
  • 341,306
  • 83
  • 791
  • 678
Wouter van Reeven
  • 517
  • 3
  • 8
  • 16
  • No, you can't for obvious security reasons. The extension would need to expose its API on purpose – Bergi Dec 08 '12 at 13:50

3 Answers3

15

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.

Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • @Xan `chrome.extension.sendMessage` is an alias for `chrome.runtime.sendMessage`. Are you perhaps confused with `chrome.extension.sendRequest`? – Rob W Aug 10 '15 at 12:35
  • I'm not confused; but the function is retired completely (not mentioned in the docs) and yet keeps popping up in new code because of lingering old sample code. See [this issue](https://code.google.com/p/chromium/issues/detail?id=495052) to mark it as deprecated. – Xan Aug 10 '15 at 12:48
3

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);
  });
ahmed
  • 5,430
  • 1
  • 20
  • 36
1

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.

Sudarshan
  • 18,140
  • 7
  • 53
  • 61
  • Thank you for your answer. But no the deal.. Can I also ron these function inside the myscript.js function clearhist () { var millisecondsPerWeek = 1000 * 60 * 60 * 24 * 7; var oneWeekAgo = (new Date()).getTime() - millisecondsPerWeek; chrome.browsingData.remove({ "since": oneWeekAgo }, { "appcache": true, "cache": true, "cookies": true, "downloads": true, "fileSystems": true, "formData": true, "history": true, "indexedDB": true, "localStorage": true, "pluginData": true, "passwords": true, "webSQL": true }, callback); } – Wouter van Reeven Dec 08 '12 at 13:53
  • @WoutervanReeven: No, you can not put this code directly in `myscript.js`, but you can achieve this by making indirect call to code by placing this in `background` page and through message communication. refer [THIS](http://stackoverflow.com/questions/13637715/not-receiving-any-data-from-webpage-to-content-js-of-chrome-extension/13638508#13638508) let me know if you need more information – Sudarshan Dec 08 '12 at 14:08
  • So I need to call the javascript function in the background js script. In the html of page http://www.example.com/test.html in the to do something to call the script inside the chrome extension. Background html myscript.js hello(); – Wouter van Reeven Dec 08 '12 at 14:15
  • @WoutervanReeven: Yes, put the code for `browsing data API` in `background js`, and call the function from content script using message communication; Refer [THIS](http://stackoverflow.com/questions/13557159/googlechrome-extension-that-deletes-browsing-history-with-one-click-from-an-icon/13557728#13557728) for more information – Sudarshan Dec 08 '12 at 14:21
  • @WoutervanReeven: You can not directly make a call with hello(); it has to be invoked through message communication . – Sudarshan Dec 08 '12 at 14:32