谷歌浏览器扩展:如何在页面重新加载后立即注入脚本? [英] Google chrome extension: how to inject script immediately after page reload?

查看:91
本文介绍了谷歌浏览器扩展:如何在页面重新加载后立即注入脚本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个后台脚本,它会定期重新加载当前选项卡.

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.

我的目标是在 DOM 加载后立即执行脚本.有什么想法吗?

My goal is to execute the script immediately after DOM load. Any ideas?

推荐答案

使用 manifest.json content_scripts 条目和 run_at": document_start";

使用 manifest.json content_scripts 条目和 "run_at":"document_start" 是您可以保证您的内容脚本在页面存在之前注入.此时注入的代码会发现document.bodydocument.head都会是null.

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 both document.body and document.head will be null.

调用tabs.executeScript() 位于 webRequest.onHeadersReceivedwebNavigation.onBeforeNavigate 事件之后触发的事件用于您感兴趣的页面导航.在此事件之前使用 tabs.executeScript() 可能会导致您的脚本不会被注入到新页面中,而不会报告任何错误.鉴于后台脚本和页面加载过程之间的时间固有的异步性质,此类故障可能是间歇性的,并且会受到操作系统/系统配置和正在加载的确切页面的影响.事实上,我关于 webRequest.onHeadersReceived 将起作用的声明是基于测试,而不是在 Chrome 源代码中进行验证.因此,可能存在我没有测试的极端情况.

The earliest time that works to call tabs.executeScript() is in the webRequest.onHeadersReceived event that fires after the webNavigation.onBeforeNavigate event for the page navigation you are interested in. Using tabs.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 that webRequest.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.

此时注入始终有效,但与页面加载相关的注入发生时间有些不一致.有时,document.headdocument.body 将是 null(对于 manifest.json content_scripts 注入 run_at":document_start").其他时候,document.headdocument.body 将包含一个有效的 DOM.由于 nulldocument.headdocument.body 始终为 null后台脚本和内容在不同的进程中:因此,本质上是异步的.

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 and document.body will be null (as will always be the case with a manifest.json content_scripts injection with "run_at":"document_start"). Other times, the document.head and document.body will contain a valid DOM. It does not appear to be possible to get this timed any tighter (i.e. always have document.head and document.body be null) due to the background script and the content being in different processes: thus, inherently asynchronous.

使用 webNavigation.onBeforeNavigate至少等待相关的 webRequest.onHeadersReceived 事件为时过早且不起作用(内容脚本未注射).换句话说,即使是相关的 webRequest.onSendHeaders 事件为时尚早.

Using webNavigation.onBeforeNavigate and not waiting for at least the associated webRequest.onHeadersReceived event is too early and does not function (content script not injected). In other words, even the associated webRequest.onSendHeaders event is too early.

显然,要尽早注入内容脚本,您必须指定 runAt:'document_start' 在您对 tabs.executeScript() 的调用中.

Obviously, to get the content script injected that early, you have to specify runAt:'document_start' in your call to tabs.executeScript().

如果您想要更简单的东西,并且会导致在页面中除主 HTML 文档之外的任何内容之前注入内容脚本,那么您可以只使用 webNavigation.onCommitted 事件,用于触发您的 tabs.executeScript() 所需 URL 的事件代码>.这将导致注入的内容脚本在主 HTML 文档之后立即加载.使用 webNavigation.onCommitted 变得更加容易,因为它能够为您的事件指定过滤器.因此,您可以只为您感兴趣的 URL 调用您的事件侦听器.

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 your tabs.executeScript(). This will result in the injected content script being loaded immediately after the main HTML document. Using webNavigation.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.

webNavigation.onCommitted 事件在主 HTML 页面的 webRequest.ResponseStarted 事件之后触发,但在获取任何资源之前(即在任何 webRequest.BeforeRequest 页面资源的事件).有趣的是,它确实在声明 status:'loading'tabs.onUpdated 事件之后触发.带有 status:'loading'tabs.onUpdated 事件本身并不是一个好的触发事件,因为它可以因具有相同属性的其他原因触发,但不会触发'不表示页面加载/重新加载.

The webNavigation.onCommitted event fires after the webRequest.ResponseStarted event for the main HTML page, but before any resources are fetched (i.e. prior to any webRequest.BeforeRequest events for page resources). Interestingly, it does fire after the tabs.onUpdated event that declares a status:'loading'. The tabs.onUpdated event with status:'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.

webNavigation.onCommitted 事件侦听器接收一个属性:transitionType,这将是不同的值基于导航的原因.其中一个值是 'reload',您可以使用它来过滤仅页面重新加载.

The 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.

鉴于您对页面重新加载感兴趣,而不是加载框架,您需要确保 webNavigation 事件用于 frameId:0.

Given that you are interested in page reload, and not loading frames, you will want to make sure that the webNavigation events are for frameId:0.

这些是当您通过单击重新加载此页面"重新加载选项卡时发生的事件按钮:

These are the events which occur when you reload a tab by clicking on the "reload this page" button:

webNavigation.onBeforeNavigate    ->  arg[0]= {"frameId":0,"parentFrameId":-1,"processId":-1,"tabId":411,"timeStamp":1500401223978.314,"url":"http://www.example.com/"}        
webRequest.onBeforeRequest        ->  arg[0]= {"frameId":0,"method":"GET","parentFrameId":-1,"requestId":"260870","tabId":411,"timeStamp":1500401223979.044,"type":"main_frame","url":"http://www.example.com/"}        
webRequest.onBeforeSendHeaders    ->  arg[0]= {"frameId":0,"method":"GET","parentFrameId":-1,"requestHeaders":[{"name":"Upgrade-Insecure-Requests","value":"1"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"},{"name":"Accept","value":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Accept-Language","value":"en-US,en;q=0.8"}],"requestId":"260870","tabId":411,"timeStamp":1500401223979.3242,"type":"main_frame","url":"http://www.example.com/"}        
webRequest.onSendHeaders          ->  arg[0]= {"frameId":0,"method":"GET","parentFrameId":-1,"requestHeaders":[{"name":"Upgrade-Insecure-Requests","value":"1"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"},{"name":"Accept","value":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Accept-Language","value":"en-US,en;q=0.8"}],"requestId":"260870","tabId":411,"timeStamp":1500401223979.538,"type":"main_frame","url":"http://www.example.com/"}        
webRequest.onHeadersReceived      ->  arg[0]= {"frameId":0,"method":"GET","parentFrameId":-1,"requestId":"260870","responseHeaders":[{"name":"Content-Encoding","value":"gzip"},{"name":"Accept-Ranges","value":"bytes"},{"name":"Cache-Control","value":"max-age=604800"},{"name":"Content-Type","value":"text/html"},{"name":"Date","value":"Tue, 18 Jul 2017 18:07:03 GMT"},{"name":"Etag","value":""359670651""},{"name":"Expires","value":"Tue, 25 Jul 2017 18:07:03 GMT"},{"name":"Last-Modified","value":"Fri, 09 Aug 2013 23:54:35 GMT"},{"name":"Server","value":"ECS (rhv/818F)"},{"name":"Vary","value":"Accept-Encoding"},{"name":"X-Cache","value":"HIT"},{"name":"Content-Length","value":"606"}],"statusCode":200,"statusLine":"HTTP/1.1 200 OK","tabId":411,"timeStamp":1500401224072.296,"type":"main_frame","url":"http://www.example.com/"}        
---^^^^^^^^^^^^^^^^^^^^^^^^^-Earliest tabs.executeScript() injection is in the handler for the webRequest.onHeadersReceived event.
webRequest.onResponseStarted      ->  arg[0]= {"frameId":0,"fromCache":false,"ip":"93.184.216.34","method":"GET","parentFrameId":-1,"requestId":"260870","responseHeaders":[{"name":"Content-Encoding","value":"gzip"},{"name":"Accept-Ranges","value":"bytes"},{"name":"Cache-Control","value":"max-age=604800"},{"name":"Content-Type","value":"text/html"},{"name":"Date","value":"Tue, 18 Jul 2017 18:07:03 GMT"},{"name":"Etag","value":""359670651""},{"name":"Expires","value":"Tue, 25 Jul 2017 18:07:03 GMT"},{"name":"Last-Modified","value":"Fri, 09 Aug 2013 23:54:35 GMT"},{"name":"Server","value":"ECS (rhv/818F)"},{"name":"Vary","value":"Accept-Encoding"},{"name":"X-Cache","value":"HIT"},{"name":"Content-Length","value":"606"}],"statusCode":200,"statusLine":"HTTP/1.1 200 OK","tabId":411,"timeStamp":1500401224072.5032,"type":"main_frame","url":"http://www.example.com/"}        
webRequest.onCompleted            ->  arg[0]= {"frameId":0,"fromCache":false,"ip":"93.184.216.34","method":"GET","parentFrameId":-1,"requestId":"260870","responseHeaders":[{"name":"Content-Encoding","value":"gzip"},{"name":"Accept-Ranges","value":"bytes"},{"name":"Cache-Control","value":"max-age=604800"},{"name":"Content-Type","value":"text/html"},{"name":"Date","value":"Tue, 18 Jul 2017 18:07:03 GMT"},{"name":"Etag","value":""359670651""},{"name":"Expires","value":"Tue, 25 Jul 2017 18:07:03 GMT"},{"name":"Last-Modified","value":"Fri, 09 Aug 2013 23:54:35 GMT"},{"name":"Server","value":"ECS (rhv/818F)"},{"name":"Vary","value":"Accept-Encoding"},{"name":"X-Cache","value":"HIT"},{"name":"Content-Length","value":"606"}],"statusCode":200,"statusLine":"HTTP/1.1 200 OK","tabId":411,"timeStamp":1500401224074.0261,"type":"main_frame","url":"http://www.example.com/"}        
tabs.onUpdated                    ->  arg[0]= 411 :: arg[1]= {"status":"loading","url":"http://www.example.com/"} :: arg[2]= {"active":true,"audible":false,"autoDiscardable":true,"discarded":false,"height":902,"highlighted":true,"id":411,"incognito":false,"index":1,"mutedInfo":{"muted":false},"pinned":false,"selected":true,"status":"loading","title":"www.example.com","url":"http://www.example.com/","width":1282,"windowId":10}    
tabs.onZoomChange                 ->  arg[0]= {"newZoomFactor":1,"oldZoomFactor":1,"tabId":411,"zoomSettings":{"mode":"automatic","scope":"per-origin"}}        
webNavigation.onCommitted         ->  arg[0]= {"frameId":0,"processId":107,"tabId":411,"timeStamp":1500401224079.4019,"transitionQualifiers":[],"transitionType":"reload","url":"http://www.example.com/"}
--->>Here is where you can tell it's a reload --------------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^
history.onVisited                 ->  arg[0]= {"id":"42","lastVisitTime":1500401224077.579,"title":"Example Domain","typedCount":1,"url":"http://www.example.com/","visitCount":12}        
tabs.onUpdated                    ->  arg[0]= 411 :: arg[1]= {"title":"Example Domain"} :: arg[2]= {"active":true,"audible":false,"autoDiscardable":true,"discarded":false,"height":902,"highlighted":true,"id":411,"incognito":false,"index":1,"mutedInfo":{"muted":false},"pinned":false,"selected":true,"status":"loading","title":"Example Domain","url":"http://www.example.com/","width":1282,"windowId":10}    
webNavigation.onDOMContentLoaded  ->  arg[0]= {"frameId":0,"processId":107,"tabId":411,"timeStamp":1500401224093.404,"url":"http://www.example.com/"}        
webNavigation.onCompleted         ->  arg[0]= {"frameId":0,"processId":107,"tabId":411,"timeStamp":1500401224094.768,"url":"http://www.example.com/"}        
tabs.onUpdated                    ->  arg[0]= 411 :: arg[1]= {"status":"complete"} :: arg[2]= {"active":true,"audible":false,"autoDiscardable":true,"discarded":false,"height":902,"highlighted":true,"id":411,"incognito":false,"index":1,"mutedInfo":{"muted":false},"pinned":false,"selected":true,"status":"complete","title":"Example Domain","url":"http://www.example.com/","width":1282,"windowId":10}   


注意:此信息基于我自己的测试.我没有找到来自 Google 的具有这种特殊性的文档.实际工作的确切时间可能会在未来版本的 Chrome 中发生变化.


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.

这篇关于谷歌浏览器扩展:如何在页面重新加载后立即注入脚本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆