Executing Code at Page-Level from Background.Js and Returning the Value

Executing code at page-level from Background.js and returning the value

You are quite right in understanding the 3-layer context separation.

  • A background page is a separate page and therefore doesn't share JS or DOM with visible pages.
  • Content scripts are isolated from the webpage's JS context, but share DOM.
  • You can inject code into the page's context using the shared DOM. It has access to the JS context, but not to Chrome APIs.

To communicate, those layers use different methods:

Background <-> Content talk through Chrome API.

The most primitive is the callback of executeScript, but it's impractical for anything but one-liners.

The common way is to use Messaging.

Uncommon, but it's possible to communicate using chrome.storage and its onChanged event.

Page <-> Extension cannot use the same techniques.

Since injected page-context scripts do not technically differ from page's own scripts, you're looking for methods for a webpage to talk to an extension. There are 2 methods available:

  1. While pages have very, very limited access to chrome.* APIs, they can nevertheless use Messaging to contact the extension. This is achieved through "externally_connectable" method.

    I have recently described it in detail this answer. In short, if your extension declared that a domain is allowed to communicate with it, and the domain knows the extension's ID, it can send an external message to the extension.

    The upside is directly talking to the extension, but the downside is the requirement to specifically whitelist domains you're using this from, and you need to keep track of your extension ID (but since you're injecting the code, you can supply the code with the ID). If you need to use it on any domain, this is unsuitable.

  2. Another solution is to use DOM Events. Since the DOM is shared between the content script and the page script, an event generated by one will be visible to another.

    The documentation demonstrates how to use window.postMessage for this effect; using Custom Events is conceptually more clear.

    Again, I answered about this before.

    The downside of this method is the requirement for a content script to act as a proxy. Something along these lines must be present in the content script:

    window.addEventListener("PassToBackground", function(evt) {
    chrome.runtime.sendMessage(evt.detail);
    }, false);

    while the background script processes this with a chrome.runtime.onMessage listener.

I encourage you to write a separate content script and invoke executeScript with a file attribute instead of code, and not rely on its callback. Messaging is cleaner and allows to return data to background script more than once.

I'm trying to get jquery working in the background.js of my chrome extension and it won't work. why?

You are correctly loading jQuery into the background page. If you were to use $(document).ready() in its code, it would work.

The problem is, when you use executeScript, the code you specify does not execute in the same context as background.js. A brand new JS context is created, attached to the tab in question, and that context does not have jQuery in it (even if the tab's context itself has it, by the way, see isolated context).

Furthermore, you're calling executeScript wrong. Its arguments are:

  • integer (optional) tabId
  • object details
  • function (optional) callback

You are calling it with two objects; it doesn't work like that.


So, to fix, you will need to inject jQuery first in that execution context, and then your code. You need to chain 2 calls to ensure they execute in the correct order.

chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(
tab.id,
{ file: 'jquery-2.1.1.js' },
function() {
chrome.tabs.executeScript(
tab.id,
{ code: '$( document ).ready(function() { console.log( "This works now" ); });' }
);
}
);
});

How to run javascript function in background / without freezing UI

Apparently there is no real way to run something on background...

There is on most modern browsers (but not IE9 and earlier): Web Workers.

But I think you're trying to solve the problem at the wrong level: 1. It should be possible to loop through all of your controls in a lot less than five seconds, and 2. It shouldn't be necessary to loop through all controls when only one of them has changed.

I suggest looking to those problems before trying to offload that processing to the background.

For instance, you could have an object that contains the current value of each item, and then have the UI for each item update that object when the value changes. Then you'd have all the values in that object, without having to loop through all the controls again.

Execute web page js from chrome extension content script

Chrome executes content scripts in a sandbox, so you need to inject an inline script to interact with the webpage:

var injectedCode = 'update()';
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ injectedCode +')();'));
(document.body || document.head || document.documentElement).appendChild(script);

Chrome Extension - Passing object from page to context script

The global variables of a content script and a page-level injected script are isolated.

Content scripts execute in a special environment called an isolated world. They have access to the DOM of the page they are injected into, but not to any JavaScript variables or functions created by the page. It looks to each content script as if there is no other JavaScript executing on the page it is running on. The same is true in reverse: JavaScript running on the page cannot call any functions or access any variables defined by content scripts.

Emphasis mine.

To pass the data to your content script, you don't have to employ extra DOM elements. You just need custom DOM events.

// Content script
// Listen for the event
window.addEventListener("FromPage", function(evt) {
/* message is in evt.detail */
}, false);

// Page context
var message = {/* whatever */};
var event = new CustomEvent("FromPage", {detail: message});
window.dispatchEvent(event);

See this answer for far more details.



Related Topics



Leave a reply



Submit