在 cefsharp 中拦截对 window.frameElement 的调用

Intercept calls to window.frameElement in cefsharp

在我的 WinForms 项目中,我正在打开一个 CefSharp 浏览器并加载一个不受我控制的外部 url。通常,此 URL 在外部应用程序中作为 iframe 打开。

该页面使用 window.frameElement 与其父 window 交互,并在 window.frameElement 上调用一些函数。

在 CefSharp 中,我无法删除 window.frameElement

有没有其他方法可以知道页面何时调用了 window.frameElement 上的某个函数。

这会很麻烦,但无论如何我都会分享。我创建了一个对我有用的简单示例,希望它对您也有帮助。

JavaScript 侧只是一个普通的 HTML,带有一个按钮,在单击时调用事件处理程序:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Test</title>

    <script src="app.js"></script>
</head>
<body>
    <button onclick="onClick()">Click me</button>
</body>
</html>

事件处理程序只是调用一个带有一些参数的随机函数(甚至不必存在于 frameElement 上):

app.js

function onClick() {
    var x = window.frameElement.greet("Hello", "world");
}

C#端有一个ChromiumWebBrowser指向我页面的地址:

Form1.Designer.cs

this.browser = new CefSharp.WinForms.ChromiumWebBrowser("http://localhost:3000/index.html");

我订阅了FrameLoadEnd活动:

this.browser.FrameLoadEnd += BrowserOnFrameLoadEnd;

处理程序将一些 JavaScript 代码注入 main/top 框架(我们的主页):

Form1.cs

private void BrowserOnFrameLoadEnd(object sender, FrameLoadEndEventArgs frameLoadEndEventArgs)
{
    if (frameLoadEndEventArgs.Frame.IsMain)
    {
        frameLoadEndEventArgs.Frame.ExecuteJavaScriptAsync(@"
(function() {
    Object.defineProperty(window, 'frameElement', {
        writable: true
    });

    window.frameElement = {};

    window.frameElement = new Proxy(window.frameElement, {
        get: function (target, propKey, receiver) {
            return function(...args) {
                window.frameElementInterceptor.intercept(propKey, args);
            };
        }
    });
})();");
    }
}

注入的代码修改 window.frameElement 以使用 ES6 Proxy 捕获 属性 吸气剂。这样当原始页面 window.frameElement.someMethod(arg1, arg2) 我可以注入我的自定义逻辑。

我正在注入的自定义逻辑是调用 frameElementInterceptorintercept 方法,其中包含被调用的函数的名称和传入的参数。

回到C#端,我们在CEFSharp浏览器中注册了一个JS对象,并在JS的全局范围内以frameElementInterceptor的形式提供:

[ComVisible(true)]
public class FrameElementInterceptor
{
    public void Intercept(string propKey, object[] args)
    {
        MessageBox.Show($@"Function '{propKey}' was called with arguments: [{string.Join(", ", args)}]",
@"Function call intercepted");
    }
}

public Form1()
{
    InitializeComponent();

    // .....

    browser.RegisterJsObject("frameElementInterceptor", new FrameElementInterceptor());
}

现在所有部分都已准备就绪,每当我单击页面中的按钮时,我的 C# 拦截器都会收到消息:

警告:虽然这个简单的例子可行,但我确信这里没有考虑各种边缘情况,所以我会非常谨慎地将其投入生产. 此外,我认为更好的解决方案将涉及 CefSharp 方面的代码更改。他们可以启用从 DynamicObject 派生的类型的注册,并启用覆盖 window 上的现有对象(如 frameElement)。据我所知,自 this question.

以来,这方面没有任何进展