在页面中执行一个来自内容脚本的页面脚本,优先于页面中其他JavaScript的执行。

3
我正在尝试分析一些JavaScript代码,为此我使用函数重写,以便将对JavaScript库的调用通过我的JavaScript代码进行。我的JavaScript代码是Chrome扩展程序的一部分。从Chrome扩展程序内容脚本中,我将代码安装/注入到目标页面的DOM中。
这适用于在页面加载后引入的函数。库调用通过我的函数进行。但是,在页面实际加载时(可能在渲染DOM时),会运行一些JavaScript代码。这发生在注入自定义脚本之前。因此,在注入自定义脚本之前的函数调用对我来说不可见,或者这些JavaScript调用不经过我的函数。
我利用Content Script通过将其附加到DOM中来实际注入其他JavaScript,如下面的Stack Exchange问题所述: 通过脚本内容将代码插入页面上下文 我知道我可以使Content Script的加载时间在DOM的开头/结尾,但这是我附加到目标页面的另一个脚本文件。我似乎不明白如何控制它。
使用Chrome扩展程序是否可以在网页的任何脚本运行之前在网页上下文中运行脚本?中解释的问题是完全相同的,但解决方案似乎不起作用。我的意图是使注入的脚本在网页的任何JavaScript代码执行之前执行。通过在manifest.json中指定document_start,可以使内容脚本执行在网页之前运行,但不能使我通过内容脚本注入的脚本运行(如第一个链接中所述)。这个注入的脚本没有以任何特定的方式运行与网页相关。
Manifest.json:
清单文件将内容脚本content.js添加到document_start,因此在目标网页(基础页面)运行之前运行content.js。
"content_scripts":[
    {
    "matches":["<all_urls>"],            
    "js":["content.js"],
    "run_at":"document_start",
    "all_frames":false
    }
],

content.js:

content.js有以下代码,我将main.js添加到DOM中,这样我就能够与目标页面环境中的JavaScript进行交互。我从不同的文件中执行此操作并将其附加到DOM中,因为我不能通过Content Scripts与目标页面的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运行的内容脚本来注入它的。

假设我在目标页面HTML中调用了一些JavaScript函数,例如someJSCall()是由目标页面的域定义的。

<html onLoad="someJSCall( )">

在这种情况下,通过我的Chrome扩展程序注入的代码main.js已经可用。因此,从someJSCall()函数对JavaScript库的调用将通过main.js包装函数进行。这很好地工作。
问题出现在目标页面的JavaScript中定义了IIFE(立即调用的函数表达式)。如果这些IIFE调用进行库调用,则不会通过我的main.js拦截。如果我通过Chrome Dev Tools查看在浏览器中加载的文件,则在执行IIFE调用时仍未加载main.js。
我希望我已经详细解释了问题。

编辑了问题以添加细节。感谢您在那里查看问题! - Bhargava N.B
@BhargavaN.B 如果你想在评论中通知某人,你需要使用 @[用户名] 来提醒他们。对于我来说,这将是 @Makyen。在这种情况下,直到现在,也就是2.5年后,我才知道你已经更新了问题。 - Makyen
有了这些额外的细节,我同意这不是2016年提出的那个重复问题的重复。 - Makyen
1个回答

0

根据你在我回答问题约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()

以下是我基于原始版本的问题的原始答案,该版本未显示内容脚本何时被执行,也没有解释添加到页面中的代码位于单独的文件中,而不是实际的内容脚本中。

你在问题中提到了:"可以处理Content Script的加载时间,将其放在DOM的开头/结尾",但你并没有明确说明为什么无法通过在document_start执行你的内容脚本来解决问题。

你可以通过在manifest.json content_scripts条目中指定run_at属性为document_start,或者在chrome.tabs.executeScript()中传递runAt选项的方式,在注入页面构建之前注入你的脚本。如果这样做,那么当document.headdocument.body都是null时,你的脚本就会开始运行。然后,你可以控制添加到页面上的内容。

对于chrome.tabs.executeScript(),您的脚本运行的确切时间取决于您何时相对于页面加载过程执行chrome.tabs.executeScript()。由于处理过程具有异步性(您的后台脚本通常在不同的进程中运行),因此很难在document.headdocument.body都为null时始终注入您的脚本。我已经做到的最好的事情是在这种情况下有时注入脚本,在页面填充之后但在获取其他任何资源之前有时注入脚本。这个时间安排对大多数事情都有效,但是如果您真的需要让您的脚本在页面存在之前运行,那么您应该使用一个manifest.jsoncontent_scripts条目。

使用内容脚本早于headbody的存在,您可以控制首先插入什么内容。因此,您可以在页面上的任何其他内容之前插入您的<script>。这应该使您的脚本在页面上下文中的任何其他脚本之前执行。


感谢您的输入。我实际上面临的问题是,我希望调用js库通过我的javascript(函数重写,就像在库调用上的around advice)。由于内容脚本不干扰常规JS调用,所以我使用先前提供的链接中描述的方法将其注入到网页上下文中。这是通过将其附加到DOM来完成的。问题是 - Bhargava N.B
问题在于当我遇到在html页面中加载js时会自动调用自身的IIFE调用时。如果库调用发生在<body onLoad=""> 时,一切都正常,我的建议已经被注入了。但是当使用IIFE时,它们在我的建议JS被注入之前运行。希望我的问题比之前更清楚,再次感谢。 - Bhargava N.B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接