我正在尝试分析一些 JavaScript 代码,我使用函数重写来调用 JavaScript 库,以便通过我的 JavaScript 代码进行调用。我的 JavaScript 代码是 Chrome 扩展的一部分。我通过 Chrome 扩展内容脚本将代码安装/注入(inject)到目标页面的 DOM 中。
这对于页面加载后引发的函数效果很好。库调用通过我的函数。但是,JavaScript 代码会在页面实际加载时运行(可能在渲染 DOM 时)。这发生在注入(inject)我的自定义脚本之前。这样,在注入(inject)自定义脚本之前的函数调用就会丢失,或者那些 JavaScript 调用不会通过我的函数。
我利用 Content Script 通过附加到 DOM 来实际注入(inject)其他 JavaScript,如以下 Stack Exchange 问题中所述:
Insert code into the page context using a content script
我知道我可以使内容脚本的加载时间位于 DOM 的开始/结束处,但这是我附加到目标页面 DOM 的另一个脚本文件。我似乎不明白如何控制它。
问题在 Is it possible to run a script in context of a webpage, before any of the webpage's scripts run, using a chrome extension? 中进行了解释
完全一样,但是解决方案似乎不起作用。我的目的是让注入(inject)的脚本在网页执行任何 JavaScript 代码之前执行。通过在manifest.json中指定document_start
,可以使内容脚本执行在网页之前运行,但不是我通过内容脚本注入(inject)的脚本(如第一个链接中所述注入(inject)脚本)。该注入(inject)的脚本没有以任何特定的方式针对网页运行
Manifest.json:
Manifest 文件在 document_start
处添加了内容脚本 content.js,因此 content.js 在目标网页(底层页面)运行之前运行。
"content_scripts":[
{
"matches":["<all_urls>"],
"js":["content.js"],
"run_at":"document_start",
"all_frames":false
}
],
内容.js:
content.js 具有以下代码,我可以使用这些代码将 main.js 添加到 DOM,以便我实际上能够与目标页面环境中的 JavaScript 进行交互。我从不同的文件执行此操作并将其附加到 DOM,因为我无法通过内容脚本与目标页面的 JavaScript 进行交互,因为它们不会互相干扰。
进一步解释一下,main.js 有一些 JavaScript,可以在目标页面执行 JavaScript 期间拦截 JavaScript 调用。目标页面中的 JavaScript 调用库,我只想为这些库函数编写一个包装器。
var u = document.createElement('script');
u.src = chrome.extension.getURL('main.js');
(document.head||document.documentElement).appendChild(u);
u.onload = function() {
u.parentNode.removeChild(u);
};
我希望 main.js 在目标页面的域和目标页面中的任何脚本中可用,因为我通过在 document_start
运行的内容脚本注入(inject)它。
假设我在目标页面 HTML 中调用了类似这样的 JavaScript 函数,someJSCall()
由目标页面的域定义。
<html onLoad="someJSCall( )">
在这种情况下,main.js(通过我的 Chrome 扩展注入(inject)的代码)已经可用。因此,从 someJSCall()
函数调用 JavaScript 库会通过 main.js 包装函数。
这工作得很好。
问题在于目标页面的 JavaScript 中定义了 IIFE(立即调用函数表达式)。如果这些 IIFE 调用进行库调用,则不会经过我的 main.js 拦截。如果我通过 Chrome 开发工具查看浏览器中加载的文件,我会发现在执行 IIFE 调用时 main.js 仍未加载。
我希望我已经详细解释了这个问题。
最佳答案
根据您在我回答后大约 2.5 周添加到问题中的附加信息,您通过包含“main.js”(扩展程序中的一个单独文件)向页面上下文添加代码,使用<script>
” 看起来像这样:
<script src="URL_to_file_in_extension/main.js"/>
但是,当您这样做时,您会在将<script>
插入页面与从扩展中获取“main.js”并在页面上下文中执行之间引入异步延迟。您将无法控制此延迟的时间,并且可能会也可能不会导致您的代码先于页面中的任何特定代码运行。它可能会先于必须从外部 URL 获取的代码运行,但也可能不会。
为了保证您的代码同步运行,您必须将其作为实际代码插入<script>
标签中,而不是使用src
属性拉入另一个文件。这意味着您要在页面中执行的代码必须存在于您加载到页面中的内容脚本文件中。
需要在页面上下文中执行代码是一个相当常见的需求。我需要在浏览器扩展(例如 Chrome、Firefox、Edge 等)和用户脚本中执行此操作。我还希望能够将数据传递给此类代码,因此我编写了一个名为executeInPage()
的函数,它将采用当前上下文中定义的函数,将其转换为文本,将其插入到页面上下文中并执行它同时传递您所拥有的任何参数(大多数类型)。有兴趣的话可以在我对Calling webpage JavaScript methods from browser extension的回答和我对How to use cloneInto in a Firefox web extension?的回答中找到executeInPage()
以下是我根据问题原始版本的原始答案,它没有显示内容脚本何时被执行,也没有解释添加到页面的代码是在一个单独的文件中,而不是在实际的文件中内容脚本。
您在问题中声明“可以将内容脚本的加载时间处理在 DOM 的开头/结尾”,但您没有明确说明为什么无法通过执行内容来解决问题脚本位于document_start
。
您可以在构建要注入(inject)的页面之前注入(inject)脚本,方法是为 document_start
manifest.json条目中的 run_at
属性指定content_scripts
,或者为传递给 runAt
。如果您这样做,那么您的脚本将在chrome.tabs.executeScript()
和document.head
均为document.body
时开始运行。然后,您可以控制添加到页面的内容。
对于null
,您的脚本运行的确切时间取决于您何时执行与加载页面过程相关的chrome.tabs.executeScript()
。由于处理的异步性质(您的后台脚本通常在不同的进程中运行),当chrome.tabs.executeScript()
和document.head
都是document.body
时,很难让脚本一致地注入(inject)。我所做的最好的事情是有时在这种情况下注入(inject)脚本,有时在填充页面之后但在获取任何其他资源之前注入(inject)脚本。此时间适用于大多数情况,但如果您确实需要在页面存在之前运行脚本,那么您应该使用 manifest.json null
条目。
通过在content_scripts
和head
存在之前运行您的内容脚本,您可以控制首先插入的内容。因此,您可以在页面上的其他任何内容之前插入body
。这应该使您的脚本先于页面上下文中的任何其他脚本执行。
关于javascript - 执行页面脚本,该脚本是在页面中的其他 JavaScript 之前从内容脚本插入到页面中的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41246566/