I have a background script that periodically reloads the current tab.
var code = 'window.location.reload();';
chrome.tabs.executeScript(my_active_tab, {code: code});
After each page reload, immediately I want to inject another script.
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab){
if (changeInfo.status == 'complete') {
chrome.tabs.executeScript(tabId, { file: "my_script.js" });
}
});
Above is the code I have at the moment. The thing is, it works but it is too slow, because it literally waits after every single image has been loaded.
My goal is to execute the script immediately after DOM load. Any ideas?
Use a manifest.json
content_scripts
entry with"run_at": "document_start"
Using a manifest.json
content_scripts
entry with"run_at": "document_start"
is the only way that you can guarantee your content script is injected prior to the page existing. Code injected at this time will find bothdocument.body
anddocument.head
will benull
.Earliest possible time to inject using
tabs.executeScript()
The earliest time that works to call
tabs.executeScript()
is in thewebRequest.onHeadersReceived
event that fires after thewebNavigation.onBeforeNavigate
event for the page navigation you are interested in. Usingtabs.executeScript()
prior to this event may result in your script not being injected in the new page without any reported error. Given the inherent asynchronous nature of the timing between your background script and the process of loading the page, such failures can be intermittent, and will be affected by both OS/system configuration and the exact page that's being loaded. In fact, my statement thatwebRequest.onHeadersReceived
will work is based on testing, as opposed to verifying in the Chrome source code. As a result, there may be corner cases which I did not test.Injecting at that point consistently works, but the time at which the injection occurs with respect to page loading is somewhat inconsistent. Sometimes, the
document.head
anddocument.body
will benull
(as will always be the case with a manifest.jsoncontent_scripts
injection with"run_at":"document_start"
). Other times, thedocument.head
anddocument.body
will contain a valid DOM. It does not appear to be possible to get this timed any tighter (i.e. always havedocument.head
anddocument.body
benull
) due to the background script and the content being in different processes: thus, inherently asynchronous.Using
webNavigation.onBeforeNavigate
and not waiting for at least the associatedwebRequest.onHeadersReceived
event is too early and does not function (content script not injected). In other words, even the associatedwebRequest.onSendHeaders
event is too early.Obviously, to get the content script injected that early, you have to specify
runAt:'document_start'
in your call totabs.executeScript()
.To inject just after the DOM exists:
webNavigation.onCommitted
If you want something that is easier, and will result in the content script being injected prior to anything in the page other than the main HTML document, then you can just use the
webNavigation.onCommitted
event for the desired URL to trigger yourtabs.executeScript()
. This will result in the injected content script being loaded immediately after the main HTML document. UsingwebNavigation.onCommitted
is made easier because it has the ability to specify a filter for your event. Thus, you can have your event listener only be called for the URLs you are interested in.The
webNavigation.onCommitted
event fires after thewebRequest.ResponseStarted
event for the main HTML page, but before any resources are fetched (i.e. prior to anywebRequest.BeforeRequest
events for page resources). Interestingly, it does fire after thetabs.onUpdated
event that declares astatus:'loading'
. Thetabs.onUpdated
event withstatus:'loading'
is not a good one to trigger on by itself, because it can fire for other reasons with identical properties, but which don't indicate a page load/reload.If you only want to
tabs.executeScript()
upon a page reloadThe
webNavigation.onCommitted
event listener receives a property:transitionType
, which will be different values based on the cause of the navigation. One of those values is'reload'
, which you could use to filter for only page reloads.Given that you are interested in page reload, and not loading frames, you will want to make sure that the
webNavigation
events are forframeId:0
.These are the events which occur when you reload a tab by clicking on the "reload this page" button:
Note: This information is based on my own testing. I have found no documentation from Google with this level of specificity. The exact timing of what actually works may change in future versions of Chrome.