Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exactly when does the background script in a chrome extension get run?

In my chrome extension, I have a background script that will fetch some data that it will need using a XMLHttpRequest.

// note that this code is in the global scope i.e. outside of any function
// also note that I got this code from the page talking about XMLHttpRequest
var myData = [];

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange;
xhr.open("GET", "...", true);
xhr.send();

function handleStateChange() {
  myData = Object.values(JSON.parse(xhr.responseText));
}

I want to know when will xor.send() be run.

I observed that every time I reload the extension by pressing on the enter image description here button, xhr.send() will be called. I also observed that opening a new tab/window doesn't cause the background script to be run again.

I also found this page, that the background page gets "loaded" and "unloaded", but it says very little about when the code in the global scope of the background script is run.

Does it only run when the extension is installed/reloaded?

like image 444
Sweeper Avatar asked Nov 10 '18 12:11

Sweeper


People also ask

What does background script do 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.

Does Chrome extension work in background?

This extension simply keeps Chrome running in the background, so that push notifications can be received while the browser is closed.

What does a background script do?

Events are browser triggers, such as navigating to a new page, removing a bookmark, or closing a tab. Extensions monitor these events in their background script, then react with specified instructions. A background page is loaded when it is needed, and unloaded when it goes idle.

What is the difference between content script and background script?

Background Script - Provides persistence and handles background events. Content Script - Scripts that run in isolation in the context of the web page. Injected Script - Scripts that are programmatically injected into the web page.


1 Answers

task manager pid and columns

Since only one copy of an extension's background page exists globally for all of your user's tabs and windows (responding to all per-tab resources), you will never see the start process (except during browser restarts and updates) if it is never being suspended. You can launch the task manager and see if the extension's background is always present and keeps the same Process ID indicating it is not being shut down. There is also an optional Keepalive count column, that shows how many activities are holding a process active, tasks with a - may be being forced persistent, but seems to occur for multiple reasons.

enter image description here

suspend never occurring

If the background page has persistent:false and meets all the other criteria to shut it down then it can be shutdown until the next event occurs (listener, getBackgroundPage(), etc). The next event to require it would then load the background page executing the global scope, etc as part of setting up the listeners expected to be invoked.

You can go to chrome://extensions enable developer mode and then inspect the extension's background page to view persistent and permissions: [chrome.webRequest] as they interfere:

enter image description here

enter image description here

If you still get persistent:true behavior without explicit settings causing it, then it might be caused by state in your background page's global scope. It is best to still follow the migration guide, for example your xhr request belongs in a startup if you want to persist the data from first start beyond suspends:

chrome.runtime.onStartup.addListener(function() {

    var xhr = new XMLHttpRequest()
    xhr.onreadystatechange = handleStateChange
    xhr.open("GET", "...", true)
    xhr.send()
    function handleStateChange() {
        chrome.storage.local.set({ myData: 
        JSON.stringify(Object.values(JSON.parse(xhr.responseText)))});
    }
  })

This should have roughly the equivalent behavior of running this code in the global scope with persistent:true, but the xhr can be garbage collected as this is not a scope of other listeners etc. (Since chrome marks resource types like network sockets as reasons it can not suspend, it is important to get them out of scope.) After adapting, you can test behavior from the reloading the extension in the background page inspection even if the browser is still not suspending it automatically.

If you don't want to adapt to persistent:false, then I would set persistent:true in the manifest rather than rely on current implicit behavior. (Even if you are unable to cause a suspend on your test system, systems with more memory pressure or some other condition might unload your background pages if you set persistent:false.)

suspend occurring but causing your code no problems

If you find the Process ID is changing, but you are having no problems then you are a little lucky. The system makes sure your global scope runs whenever it restarts the background page for you for an incoming request, but it does not make sure any asynchronous parts have completed. For example, it must start the ajax if it needs to start your background page to see if you have an appropriate listener, but it is allowed to call that listener when the synchronous portion of the global scope has finished running which may be before the response. Consequently, you can not count on MyData if suspend is working correctly.

surfacing event handlers while still setting/using data in the global scope

If you want to keep the xhr request in the global scope and correctly support persistent:false, you would need to make sure listeners register immediately but internally wait on myData. For example, if myData was a Promise that handleStateChange() resolves, other listeners can give asynchronous responses using myData.then(..) and have an ajax response from the background page's most recent restart instead of stored in chrome local storage since ~installation.

like image 99
lossleader Avatar answered Oct 23 '22 03:10

lossleader