forked from akuan333/CefSharp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWebBrowserExtensionsEx.cs
269 lines (227 loc) · 11.4 KB
/
WebBrowserExtensionsEx.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// Copyright © 2021 The CefSharp Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
using CefSharp.Internals;
using CefSharp.ModelBinding;
using System;
using System.IO;
using System.Threading.Tasks;
namespace CefSharp
{
/// <summary>
/// Extended WebBrowserExtensions
/// </summary>
public static class WebBrowserExtensionsEx
{
/// <summary>
/// Retrieve the current <see cref="NavigationEntry"/>. Contains information like
/// <see cref="NavigationEntry.HttpStatusCode"/> and <see cref="NavigationEntry.SslStatus"/>
/// </summary>
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
/// <returns>
/// <see cref="Task{NavigationEntry}"/> that when executed returns the current <see cref="NavigationEntry"/> or null
/// </returns>
public static Task<NavigationEntry> GetVisibleNavigationEntryAsync(this IChromiumWebBrowserBase browser)
{
var host = browser.GetBrowserHost();
if (host == null)
{
return Task.FromResult<NavigationEntry>(null);
}
if(Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
{
var entry = host.GetVisibleNavigationEntry();
return Task.FromResult<NavigationEntry>(entry);
}
var tcs = new TaskCompletionSource<NavigationEntry>(TaskCreationOptions.RunContinuationsAsynchronously);
Cef.UIThreadTaskFactory.StartNew(delegate
{
var entry = host.GetVisibleNavigationEntry();
tcs.TrySetResult(entry);
});
return tcs.Task;
}
/// <summary>
/// Downloads the specified <paramref name="url"/> and calls <paramref name="completeHandler"/>
/// when the download is complete. Makes a GET Request.
/// </summary>
/// <param name="frame">valid frame</param>
/// <param name="url">url to download</param>
/// <param name="completeHandler">Action to be executed when the download is complete.</param>
public static void DownloadUrl(this IFrame frame, string url, Action<IUrlRequest, Stream> completeHandler)
{
if (!frame.IsValid)
{
throw new Exception("Frame is invalid, unable to continue.");
}
//Can be created on any valid CEF Thread, here we'll use the CEF UI Thread
Cef.UIThreadTaskFactory.StartNew(delegate
{
var request = frame.CreateRequest(false);
request.Method = "GET";
request.Url = url;
var memoryStream = new MemoryStream();
var urlRequestClient = Fluent.UrlRequestClient
.Create()
.OnDownloadData((req, stream) =>
{
stream.CopyTo(memoryStream);
})
.OnRequestComplete((req) =>
{
memoryStream.Position = 0;
completeHandler?.Invoke(req, memoryStream);
})
.Build();
var urlRequest = frame.CreateUrlRequest(request, urlRequestClient);
});
}
/// <summary>
/// Downloads the specified <paramref name="url"/> as a <see cref="T:byte[]"/>.
/// Makes a GET Request.
/// </summary>
/// <param name="frame">valid frame</param>
/// <param name="url">url to download</param>
/// <param name="urlRequestFlags">control caching policy</param>
/// <returns>A task that can be awaited to get the <see cref="T:byte[]"/> representing the Url</returns>
public static Task<byte[]> DownloadUrlAsync(this IFrame frame, string url, UrlRequestFlags urlRequestFlags = UrlRequestFlags.None)
{
if (frame == null)
{
throw new ArgumentNullException(nameof(frame));
}
if (!frame.IsValid)
{
throw new Exception("Frame is invalid, unable to continue.");
}
var taskCompletionSource = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
//Can be created on any valid CEF Thread, here we'll use the CEF UI Thread
Cef.UIThreadTaskFactory.StartNew(delegate
{
var request = frame.CreateRequest(false);
request.Method = "GET";
request.Url = url;
request.Flags = urlRequestFlags;
var memoryStream = new MemoryStream();
var urlRequestClient = Fluent.UrlRequestClient
.Create()
.OnDownloadData((req, stream) =>
{
stream.CopyTo(memoryStream);
})
.OnRequestComplete((req) =>
{
if (req.RequestStatus == UrlRequestStatus.Success)
{
taskCompletionSource.TrySetResult(memoryStream.ToArray());
}
else
{
taskCompletionSource.TrySetException(new Exception("RequestStatus:" + req.RequestStatus + ";StatusCode:" + req.Response.StatusCode));
}
})
.Build();
var urlRequest = frame.CreateUrlRequest(request, urlRequestClient);
});
return taskCompletionSource.Task;
}
/// <summary>
/// Toggles audio mute for the current browser.
/// If the <paramref name="browser"/> is null or has been disposed
/// then this command will be a no-op.
/// </summary>
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
public static void ToggleAudioMute(this IChromiumWebBrowserBase browser)
{
if (browser.IsDisposed || Cef.IsShutdown)
{
return;
}
_ = Cef.UIThreadTaskFactory.StartNew(delegate
{
var cefBrowser = browser.BrowserCore;
if (cefBrowser == null || cefBrowser.IsDisposed)
{
return;
}
var host = cefBrowser.GetHost();
var isAudioMuted = host.IsAudioMuted;
host.SetAudioMuted(!isAudioMuted);
});
}
/// <summary>
/// Evaluate javascript code in the context of the <paramref name="frame"/>. The script will be executed
/// asynchronously and the method returns a Task that can be awaited to obtain the result.
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <exception cref="ArgumentOutOfRangeException">Thrown when one or more arguments are outside the required range.</exception>
/// <exception cref="Exception">Thrown if a Javascript error occurs.</exception>
/// <param name="frame">The IFrame instance this method extends.</param>
/// <param name="script">The Javascript code that should be executed.</param>
/// <param name="timeout">(Optional) The timeout after which the Javascript code execution should be aborted.</param>
/// <returns>
/// <see cref="Task{T}"/> that can be awaited to obtain the result of the script execution. The <see cref="ModelBinding.DefaultBinder"/>
/// is used to convert the result to the desired type. Property names are converted from camelCase.
/// If the script execution returns an error then an exception is thrown.
/// </returns>
public static async Task<T> EvaluateScriptAsync<T>(this IFrame frame, string script, TimeSpan? timeout = null)
{
WebBrowserExtensions.ThrowExceptionIfFrameNull(frame);
if (timeout.HasValue && timeout.Value.TotalMilliseconds > uint.MaxValue)
{
throw new ArgumentOutOfRangeException("timeout", "Timeout greater than Maximum allowable value of " + UInt32.MaxValue);
}
var response = await frame.EvaluateScriptAsync(script, timeout: timeout, useImmediatelyInvokedFuncExpression: false).ConfigureAwait(false);
if (response.Success)
{
var binder = DefaultBinder.Instance;
return (T)binder.Bind(response.Result, typeof(T));
}
throw new Exception(response.Message);
}
/// <summary>
/// Evaluate some Javascript code in the context of the MainFrame of the ChromiumWebBrowser. The script will be executed
/// asynchronously and the method returns a Task encapsulating the response from the Javascript
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <exception cref="ArgumentOutOfRangeException">Thrown when one or more arguments are outside the required range.</exception>
/// <param name="browser">The IBrowser instance this method extends.</param>
/// <param name="script">The JavaScript code that should be executed.</param>
/// <param name="timeout">(Optional) The timeout after which the JavaScript code execution should be aborted.</param>
/// <returns>
/// <see cref="Task{T}"/> that can be awaited to obtain the result of the JavaScript execution.
/// </returns>
public static Task<T> EvaluateScriptAsync<T>(this IBrowser browser, string script, TimeSpan? timeout = null)
{
WebBrowserExtensions.ThrowExceptionIfBrowserNull(browser);
using (var frame = browser.MainFrame)
{
return frame.EvaluateScriptAsync<T>(script, timeout: timeout);
}
}
/// <summary>
/// Evaluate Javascript in the context of this Browsers Main Frame. The script will be executed
/// asynchronously and the method returns a Task encapsulating the response from the Javascript
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown when one or more arguments are outside the required range.</exception>
/// <typeparam name="T">Type</typeparam>
/// <param name="chromiumWebBrowser">The ChromiumWebBrowser instance this method extends.</param>
/// <param name="script">The Javascript code that should be executed.</param>
/// <param name="timeout">(Optional) The timeout after which the Javascript code execution should be aborted.</param>
/// <returns>
/// <see cref="Task{T}"/> that can be awaited to obtain the result of the script execution.
/// </returns>
public static Task<T> EvaluateScriptAsync<T>(this IChromiumWebBrowserBase chromiumWebBrowser, string script, TimeSpan? timeout = null)
{
WebBrowserExtensions.ThrowExceptionIfChromiumWebBrowserDisposed(chromiumWebBrowser);
if (chromiumWebBrowser is IWebBrowser b)
{
if (b.CanExecuteJavascriptInMainFrame == false)
{
WebBrowserExtensions.ThrowExceptionIfCanExecuteJavascriptInMainFrameFalse();
}
}
return chromiumWebBrowser.BrowserCore.EvaluateScriptAsync<T>(script, timeout);
}
}
}