Google chrome 扩展:如何在页面重新加载后立即注入脚本?
Google chrome extension: how to inject script immediately after page reload?
我有一个定期重新加载当前选项卡的后台脚本。
var code = 'window.location.reload();';
chrome.tabs.executeScript(my_active_tab, {code: code});
每次重新加载页面后,我想立即注入另一个脚本。
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab){
if (changeInfo.status == 'complete') {
chrome.tabs.executeScript(tabId, { file: "my_script.js" });
}
});
以上是我目前的代码。问题是,它可以工作,但速度太慢,因为它实际上是在加载每个图像后等待。
我的目标是在 DOM 加载后立即执行脚本。有什么想法吗?
使用 manifest.json content_scripts
条目和 "run_at": "document_start"
使用带有 "run_at": "document_start"
的 manifest.json content_scripts
条目是您可以 保证 [=102] 的唯一方法=] 您的内容脚本是在页面存在之前注入的。此时注入的代码会发现document.body
和document.head
都会是null
.
使用tabs.executeScript()
注入的最早可能时间
调用 tabs.executeScript()
的最早时间是在 webRequest.onHeadersReceived
事件中,该事件在 webNavigation.onBeforeNavigate
之后触发您感兴趣的页面导航的 事件。在此事件之前使用 tabs.executeScript()
可能会导致您的脚本未被注入新页面而不会报告任何错误。鉴于后台脚本和页面加载过程之间的时间固有的异步性质,此类故障可能是间歇性的,并且会受到 OS/system 配置和正在加载的确切页面的影响。事实上,我关于 webRequest.onHeadersReceived
将起作用的声明是基于测试,而不是在 Chrome 源代码中验证。因此,可能存在我没有测试的极端情况。
在那一点上注入始终有效,但在页面加载方面发生注入的时间有些不一致。有时,document.head
和 document.body
将是 null
(manifest.json content_scripts
injection with "run_at":"document_start"
总是如此)。其他时候,document.head
和 document.body
将包含有效的 DOM。由于后台脚本和内容处于不同的进程中,似乎不可能将这个时间安排得更紧(即总是 document.head
和 document.body
为 null
):因此,本质上是异步的。
使用 webNavigation.onBeforeNavigate
和 不 至少等待关联的 webRequest.onHeadersReceived
事件为时过早并且不起作用(未注入内容脚本)。换句话说,即使是关联的 webRequest.onSendHeaders
事件也太早了。
显然,要尽早注入内容脚本,您必须在对 tabs.executeScript()
.
的调用中指定 runAt:'document_start'
在 DOM 存在之后立即注入:webNavigation.onCommitted
如果您想要更简单的东西,并且会导致内容脚本在除主 HTML 文档以外的页面中的任何内容之前被注入,那么您可以只使用 webNavigation.onCommitted
所需 URL 的事件触发你的 tabs.executeScript()
。这将导致在 HTML 主文档之后立即加载注入的内容脚本。使用 webNavigation.onCommitted
变得更容易,因为它能够为您的事件指定过滤器。因此,您可以只为您感兴趣的 URL 调用事件侦听器。
webNavigation.onCommitted
事件在主 HTML 页面的 webRequest.ResponseStarted
事件之后触发,但在获取任何资源之前(即在页面的任何 webRequest.BeforeRequest
事件之前资源)。有趣的是,它确实在声明 status:'loading'
的 tabs.onUpdated
事件之后触发。带有 status:'loading'
的 tabs.onUpdated
事件本身不是一个很好的触发事件,因为它可以出于其他原因触发具有相同属性,但不表示页面 load/reload。
如果您只想 tabs.executeScript()
页面 重新加载
webNavigation.onCommitted
事件侦听器收到 属性: transitionType
, which will be different values 基于导航的原因。其中一个值是 'reload'
,您可以使用它来过滤仅页面重新加载。
鉴于您对页面重新加载感兴趣,而不是加载框架,您需要确保 webNavigation
事件用于 frameId:0
。
这些是当您通过单击“重新加载此页面”按钮重新加载选项卡时发生的事件:
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.
的未来版本中,实际工作的确切时间可能会发生变化
我有一个定期重新加载当前选项卡的后台脚本。
var code = 'window.location.reload();';
chrome.tabs.executeScript(my_active_tab, {code: code});
每次重新加载页面后,我想立即注入另一个脚本。
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab){
if (changeInfo.status == 'complete') {
chrome.tabs.executeScript(tabId, { file: "my_script.js" });
}
});
以上是我目前的代码。问题是,它可以工作,但速度太慢,因为它实际上是在加载每个图像后等待。
我的目标是在 DOM 加载后立即执行脚本。有什么想法吗?
使用 manifest.json content_scripts
条目和 "run_at": "document_start"
使用带有 "run_at": "document_start"
的 manifest.json content_scripts
条目是您可以 保证 [=102] 的唯一方法=] 您的内容脚本是在页面存在之前注入的。此时注入的代码会发现document.body
和document.head
都会是null
.
使用tabs.executeScript()
注入的最早可能时间
调用 tabs.executeScript()
的最早时间是在 webRequest.onHeadersReceived
事件中,该事件在 webNavigation.onBeforeNavigate
之后触发您感兴趣的页面导航的 事件。在此事件之前使用 tabs.executeScript()
可能会导致您的脚本未被注入新页面而不会报告任何错误。鉴于后台脚本和页面加载过程之间的时间固有的异步性质,此类故障可能是间歇性的,并且会受到 OS/system 配置和正在加载的确切页面的影响。事实上,我关于 webRequest.onHeadersReceived
将起作用的声明是基于测试,而不是在 Chrome 源代码中验证。因此,可能存在我没有测试的极端情况。
在那一点上注入始终有效,但在页面加载方面发生注入的时间有些不一致。有时,document.head
和 document.body
将是 null
(manifest.json content_scripts
injection with "run_at":"document_start"
总是如此)。其他时候,document.head
和 document.body
将包含有效的 DOM。由于后台脚本和内容处于不同的进程中,似乎不可能将这个时间安排得更紧(即总是 document.head
和 document.body
为 null
):因此,本质上是异步的。
使用 webNavigation.onBeforeNavigate
和 不 至少等待关联的 webRequest.onHeadersReceived
事件为时过早并且不起作用(未注入内容脚本)。换句话说,即使是关联的 webRequest.onSendHeaders
事件也太早了。
显然,要尽早注入内容脚本,您必须在对 tabs.executeScript()
.
runAt:'document_start'
在 DOM 存在之后立即注入:webNavigation.onCommitted
如果您想要更简单的东西,并且会导致内容脚本在除主 HTML 文档以外的页面中的任何内容之前被注入,那么您可以只使用 webNavigation.onCommitted
所需 URL 的事件触发你的 tabs.executeScript()
。这将导致在 HTML 主文档之后立即加载注入的内容脚本。使用 webNavigation.onCommitted
变得更容易,因为它能够为您的事件指定过滤器。因此,您可以只为您感兴趣的 URL 调用事件侦听器。
webNavigation.onCommitted
事件在主 HTML 页面的 webRequest.ResponseStarted
事件之后触发,但在获取任何资源之前(即在页面的任何 webRequest.BeforeRequest
事件之前资源)。有趣的是,它确实在声明 status:'loading'
的 tabs.onUpdated
事件之后触发。带有 status:'loading'
的 tabs.onUpdated
事件本身不是一个很好的触发事件,因为它可以出于其他原因触发具有相同属性,但不表示页面 load/reload。
如果您只想 tabs.executeScript()
页面 重新加载
webNavigation.onCommitted
事件侦听器收到 属性: transitionType
, which will be different values 基于导航的原因。其中一个值是 'reload'
,您可以使用它来过滤仅页面重新加载。
鉴于您对页面重新加载感兴趣,而不是加载框架,您需要确保 webNavigation
事件用于 frameId:0
。
这些是当您通过单击“重新加载此页面”按钮重新加载选项卡时发生的事件:
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.
的未来版本中,实际工作的确切时间可能会发生变化