在任何文件加载/处理之前,CefSharp注入Javascript



对于我正在处理的项目,我需要在开始任何网页文档处理之前注入javascript。这可以通过WebBrowser组件轻松实现,但我在使用CefSharp时遇到了困难。

这里是问题的简化,网页需要一个"InjectedObject"才能发挥作用。调用网页而不在文档顶部进行注入,或者在文档处理之前进行评估/执行,将导致:

=====失败时的html示例输出=====

isObjectPresent?

错误

=====

我需要在哪里显示网页:

=====html成功的示例输出=====

isObjectPresent?

真实

=====

<html>
<head>
<script>
isObjectPresent = typeof InjectedObject == "object";
</script>
</head>
<body>
<p>isObjectPresent?</p>
<div id="result"></div>
<script>
document.getElementById("result").innerHTML = isObjectPresent;
</script>
</body>
</html>

查看所有可用的建议会表明我应该使用LoadingStateChanged()或FrameLoadEnd()来注入脚本,即:

public void OnFrameLoadEnd(object sender, FrameLoadEndEventArgs args) {
if (args.Frame.IsMain) {
args.Frame.ExecuteJavascriptAsync("window.InjectedObject = {};");
}
}

然而,我尝试过的所有迭代,甚至使用FrameLoadStart,都会导致在文档开始处理后插入javascript。有没有真正的javascript注入的例子可以确保它在文档处理开始之前发生。(确保避免出现比赛条件/时间问题)。

作为WebBrowser组件行为的一个例子,我想要模仿的是:

private void uiWebBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) 
{
var browser = (WebBrowser)sender;
var document = browser.Document as HTMLDocument;
var head = document.getElementsByTagName("head").Cast<HTMLHeadElement>().First();
if (head != null)
{
var script = document.createElement("script") as IHTMLScriptElement;
script.text = "window.InjectedObject = {};"
if (head.firstChild != null)
{
head.insertBefore((IHTMLDOMNode)script, head.firstChild);
}
else
{
head.appendChild((IHTMLDOMNode)script;
}
}
}

任何帮助或建议都是受欢迎的,理想情况下,我希望避免通过互联网请求解析和插入下载页面,然后使用loadhtml,因为我预计我可能必须为影响主框架的所有导航操作这样做,这听起来像是一项黑客工作。

根据评论,有人建议JavaScriptV8引擎上下文足以满足上述用例。尝试从IRenderProcessMessageHandler接口实现OnContextCreated方法的结果相同。

==主窗口.xaml==

<Window x:Class="ExampleCefSharp001.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ExampleCefSharp001"
mc:Ignorable="d"
Title="MainWindow" Height="1000" Width="1100">
<Grid>
<cefSharp:ChromiumWebBrowser x:Name="uiWebView"></cefSharp:ChromiumWebBrowser>
</Grid>
</Window>

===主窗口.xaml.cs===

public partial class MainWindow : Window
{
JavascriptManager jsmanager;
public MainWindow()
{
InitializeComponent();
jsmanager = new JavascriptManager(uiWebView);
}
}
public class JavascriptManager : ILoadHandler, IRenderProcessMessageHandler
{
string injection = "window.InjectedObject = {};";
public JavascriptManager(ChromiumWebBrowser browser)
{
browser.LoadHandler = this;
browser.RenderProcessMessageHandler = this;
//  Lets just pretend this is a real url with the example html above.
browser.Address = "https://www.example.com/timingtest.htm"
}
public void OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame)
{
frame.ExecuteJavaScriptAsync(injection);
}
}

我非常感谢你的意见和建议。如果我缺少什么,请告诉我!

终于回到了这一点。严重基于以下示例:CefSharp.example/Filters/FindReplaceResponseFilter.cs

实现IRequestHandler和IResponseFilter接口:

==主窗口.xaml==

<Window x:Class="ExampleCefSharp001.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ExampleCefSharp001"
mc:Ignorable="d"
Title="MainWindow" Height="1000" Width="1100">
<Grid>
<cefSharp:ChromiumWebBrowser x:Name="uiWebView"></cefSharp:ChromiumWebBrowser>
</Grid>
</Window>

===主窗口.xaml.cs===

public partial class MainWindow : Window
{
JavascriptManager jsmanager;
public MainWindow()
{
InitializeComponent();
jsmanager = new JavascriptManager(uiWebView);
}
}
public class JavascriptManager : IRequestHandler
{
string injection = "window.InjectedObject = {};";
public JavascriptManager(ChromiumWebBrowser browser)
{
browser.RequestHandler = this;
//  Lets just pretend this is a real url with the example html above.
browser.Address = "https://www.example.com/timingtest.htm"
}
public IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
{
if (frame.IsMain && request.ResourceType == ResourceType.MainFrame) 
{
return new JavascriptInjectionFilter(injection);
}
return null;
}
}
public class JavascriptInjectionFilter : IResponseFilter
{
/// <summary>
/// Location to insert the javascript
/// </summary>
public enum Locations
{
/// <summary>
/// Insert Javascript at the top of the header element
/// </summary>
head,
/// <summary>
/// Insert Javascript at the top of the body element
/// </summary>
body
}
string injection;
string location;
int offset = 0;
List<byte> overflow = new List<byte>();
/// <summary>
/// Constructor
/// </summary>
/// <param name="injection"></param>
/// <param name="location"></param>
public JavascriptInjectionFilter(string injection, Locations location = Locations.head)
{
this.injection = "<script>" + injection + "</script>";
switch (location)
{
case Locations.head:
this.location = "<head>";
break;
case Locations.body:
this.location = "<body>";
break;
default:
this.location = "<head>";
break;
}
}
/// <summary>
/// Disposal
/// </summary>
public void Dispose()
{
//
}
/// <summary>
/// Filter Processing...  handles the injection
/// </summary>
/// <param name="dataIn"></param>
/// <param name="dataInRead"></param>
/// <param name="dataOut"></param>
/// <param name="dataOutWritten"></param>
/// <returns></returns>
public FilterStatus Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten)
{
dataInRead = dataIn == null ? 0 : dataIn.Length;
dataOutWritten = 0;
if (overflow.Count > 0)
{
var buffersize = Math.Min(overflow.Count, (int)dataOut.Length);
dataOut.Write(overflow.ToArray(), 0, buffersize);
dataOutWritten += buffersize;
if (buffersize < overflow.Count)
{
overflow.RemoveRange(0, buffersize - 1);
}
else
{
overflow.Clear();
}
}

for (var i = 0; i < dataInRead; ++i)
{
var readbyte = (byte)dataIn.ReadByte();
var readchar = Convert.ToChar(readbyte);
var buffersize = dataOut.Length - dataOutWritten;
if (buffersize > 0)
{
dataOut.WriteByte(readbyte);
dataOutWritten++;
}
else
{
overflow.Add(readbyte);
}
if (char.ToLower(readchar) == location[offset])
{
offset++;
if (offset >= location.Length)
{
offset = 0;
buffersize = Math.Min(injection.Length, dataOut.Length - dataOutWritten);
if (buffersize > 0)
{
var data = Encoding.UTF8.GetBytes(injection);
dataOut.Write(data, 0, (int)buffersize);
dataOutWritten += buffersize;
}
if (buffersize < injection.Length)
{
var remaining = injection.Substring((int)buffersize, (int)(injection.Length - buffersize));
overflow.AddRange(Encoding.UTF8.GetBytes(remaining));
}
}
}
else
{
offset = 0;
}
}
if (overflow.Count > 0 || offset > 0)
{
return FilterStatus.NeedMoreData;
}
return FilterStatus.Done;
}
/// <summary>
/// Initialization
/// </summary>
/// <returns></returns>
public bool InitFilter()
{
return true;
}
}

感谢amaitland为我指明了正确的方向,感谢上面绝大多数代码所基于的示例程序。最终结果:

<html><head></head><body><script>window.InjectedObject = {}</script>
<script>
isObjectPresent = typeof InjectedObject == "object";
</script>
<p>isObjectPresent?</p>
<div id="result"></div>
<script>
document.getElementById("result").innerHTML = isObjectPresent;
</script>
</body></html>

这满足了我对文档进行预处理的需求,在页眉顶部添加一些文本,确保在注入代码之前运行现有代码不会出现时间问题。

编辑几个小修复。添加了仅在加载大型机时插入的控制逻辑。

你的答案是正确的,你应该覆盖GetResourceResponseFilter的实现,但如果你没有以正确的方式实现接口,你最终会导致浏览器无法呈现内容,你可以继承DefaultRequestHandler并覆盖GetResourceResponseFilter(),并提供接受答案中提到的自定义过滤器,如果你只需要验证这个特定的功能:,这会更容易

public class CustomRequestHandler : DefaultRequestHandler
{
string script = "alert('hello');";
public override IResponseFilter GetResourceResponseFilter(CefSharp.IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
{
if (frame.IsMain && request.ResourceType == ResourceType.MainFrame)
{
return new JavascriptInjectionFilter(script);
}
return null;
}
}

然后将其分配给铬浏览器:

CustomRequestHandler customRequestHandler = new CustomRequestHandler();
chromeBrowser.RequestHandler = customRequestHandler ;

最新更新