Skip to content

Commit cc31e2b

Browse files
committed
Core - Add ExecuteDevToolsMethodAsync extension method
ExecuteDevToolsMethod has to be called exclusively on the CEF UI thread. This extension method will invoke into the CEF UI thread if required.
1 parent a889c47 commit cc31e2b

File tree

5 files changed

+150
-1
lines changed

5 files changed

+150
-1
lines changed

CefSharp.Core/Cef.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ namespace CefSharp
5555
_disposables = gcnew HashSet<IDisposable^>();
5656
}
5757

58+
static bool CurrentOnUiThread()
59+
{
60+
return CefCurrentlyOn(CefThreadId::TID_UI);
61+
}
62+
5863
public:
5964

6065
static property TaskFactory^ UIThreadTaskFactory;
@@ -216,6 +221,10 @@ namespace CefSharp
216221
IOThreadTaskFactory = gcnew TaskFactory(gcnew CefTaskScheduler(TID_IO));
217222
FileThreadTaskFactory = gcnew TaskFactory(gcnew CefTaskScheduler(TID_FILE));
218223

224+
//Allows us to execute Tasks on the CEF UI thread in CefSharp.dll
225+
CefThread::UiThreadTaskFactory = UIThreadTaskFactory;
226+
CefThread::CurrentOnUiThreadDelegate = gcnew Func<bool>(&CurrentOnUiThread); ;
227+
219228
//To allow FolderSchemeHandlerFactory to access GetMimeType we pass in a Func
220229
CefSharp::SchemeHandler::FolderSchemeHandlerFactory::GetMimeTypeDelegate = gcnew Func<String^, String^>(&GetMimeType);
221230

@@ -472,6 +481,8 @@ namespace CefSharp
472481
UIThreadTaskFactory = nullptr;
473482
IOThreadTaskFactory = nullptr;
474483
FileThreadTaskFactory = nullptr;
484+
CefThread::UiThreadTaskFactory = nullptr;
485+
CefThread::CurrentOnUiThreadDelegate = nullptr;
475486

476487
for each(IDisposable^ diposable in Enumerable::ToList(_disposables))
477488
{

CefSharp/CefSharp.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@
105105
<Compile Include="DefaultApp.cs" />
106106
<Compile Include="DevToolsExtensions.cs" />
107107
<Compile Include="Enums\SchemeOptions.cs" />
108+
<Compile Include="Internals\CefThread.cs" />
109+
<Compile Include="Internals\FreezableBase.cs" />
108110
<Compile Include="Internals\IBrowserRefCounter.cs" />
109111
<Compile Include="Internals\MimeTypeMapping.cs" />
110112
<Compile Include="IRegistration.cs" />

CefSharp/DevToolsExtensions.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
//
33
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
44

5+
using System.Collections.Generic;
6+
using System.Threading.Tasks;
7+
using CefSharp.Internals;
58
using CefSharp.Web;
69

710
namespace CefSharp
@@ -33,5 +36,72 @@ public static int ExecuteDevToolsMethod(this IBrowserHost browserHost, int messa
3336

3437
return browserHost.ExecuteDevToolsMethod(messageId, method, json);
3538
}
39+
40+
/// <summary>
41+
/// Execute a method call over the DevTools protocol. This is a more structured
42+
/// version of SendDevToolsMessage. <see cref="ExecuteDevToolsMethod"/> can only be called on the
43+
/// CEF UI Thread, this method can be called on any thread.
44+
/// See the DevTools protocol documentation at https://chromedevtools.github.io/devtools-protocol/ for details
45+
/// of supported methods and the expected <paramref name="parameters"/> dictionary contents.
46+
/// See the SendDevToolsMessage documentation for additional usage information.
47+
/// </summary>
48+
/// <param name="browser">the browser instance</param>
49+
/// <param name="messageId">is an incremental number that uniquely identifies the message (pass 0 to have the next number assigned
50+
/// automatically based on previous values)</param>
51+
/// <param name="method">is the method name</param>
52+
/// <param name="parameters">are the method parameters represented as a dictionary,
53+
/// which may be empty.</param>
54+
/// <returns>return a Task that can be awaited to obtain the assigned message Id. If the message was
55+
/// unsuccessfully submitted for validation, this value will be 0.</returns>
56+
public static Task<int> ExecuteDevToolsMethodAsync(this IBrowser browser, int messageId, string method, IDictionary<string, object> parameters = null)
57+
{
58+
WebBrowserExtensions.ThrowExceptionIfBrowserNull(browser);
59+
60+
var browserHost = browser.GetHost();
61+
62+
WebBrowserExtensions.ThrowExceptionIfBrowserHostNull(browserHost);
63+
64+
if (CefThread.CurrentlyOnUiThread)
65+
{
66+
return Task.FromResult(browserHost.ExecuteDevToolsMethod(messageId, method, parameters));
67+
}
68+
69+
if (CefThread.CanExecuteOnUiThread)
70+
{
71+
return CefThread.ExecuteOnUiThread(() =>
72+
{
73+
return browserHost.ExecuteDevToolsMethod(messageId, method, parameters);
74+
});
75+
}
76+
77+
//CEF returns 0 to signify failure, we'll do the same.
78+
return Task.FromResult(0);
79+
}
80+
81+
/// <summary>
82+
/// Execute a method call over the DevTools protocol. This is a more structured
83+
/// version of SendDevToolsMessage. <see cref="ExecuteDevToolsMethod"/> can only be called on the
84+
/// CEF UI Thread, this method can be called on any thread.
85+
/// See the DevTools protocol documentation at https://chromedevtools.github.io/devtools-protocol/ for details
86+
/// of supported methods and the expected <paramref name="parameters"/> dictionary contents.
87+
/// See the SendDevToolsMessage documentation for additional usage information.
88+
/// </summary>
89+
/// <param name="chromiumWebBrowser">the ChromiumWebBrowser instance</param>
90+
/// <param name="messageId">is an incremental number that uniquely identifies the message (pass 0 to have the next number assigned
91+
/// automatically based on previous values)</param>
92+
/// <param name="method">is the method name</param>
93+
/// <param name="parameters">are the method parameters represented as a dictionary,
94+
/// which may be empty.</param>
95+
/// <returns>return a Task that can be awaited to obtain the assigned message Id. If the message was
96+
/// unsuccessfully submitted for validation, this value will be 0.</returns>
97+
public static Task<int> ExecuteDevToolsMethodAsync(this IWebBrowser chromiumWebBrowser, int messageId, string method, IDictionary<string, object> parameters = null)
98+
{
99+
chromiumWebBrowser.ThrowExceptionIfDisposed();
100+
chromiumWebBrowser.ThrowExceptionIfBrowserNotInitialized();
101+
102+
var browser = chromiumWebBrowser.GetBrowser();
103+
104+
return browser.ExecuteDevToolsMethodAsync(messageId, method, parameters);
105+
}
36106
}
37107
}

CefSharp/Internals/CefThread.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright © 2020 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
using System;
6+
using System.Threading.Tasks;
7+
8+
namespace CefSharp.Internals
9+
{
10+
/// <summary>
11+
/// To access the CEF threads we expose a TaskFactory, as this requires managed vc++ this
12+
/// exists in CefSharp.Core it cannot be directly accessed in CefSharp.dll. When
13+
/// Cef.Initialized is called we pass a reference to the TaskFactory here so we
14+
/// can write methods (typically extension methods) in this assembly.
15+
/// </summary>
16+
public static class CefThread
17+
{
18+
/// <summary>
19+
/// TaskFactory will be null before Cef.Initialize is called
20+
/// and null after Cef.Shutdown is called.
21+
/// </summary>
22+
public static TaskFactory UiThreadTaskFactory { get; set; }
23+
24+
public static Func<bool> CurrentOnUiThreadDelegate { get; set; }
25+
26+
/// <summary>
27+
/// true if we have a reference to the UiThreadTaskFactory
28+
/// TaskFactory, otherwise false
29+
/// </summary>
30+
/// <remarks>
31+
/// The current implementation isn't thread safe, generally speaking this shouldn't be a problem
32+
/// </remarks>
33+
public static bool CanExecuteOnUiThread
34+
{
35+
get
36+
{
37+
return UiThreadTaskFactory != null;
38+
}
39+
}
40+
41+
public static bool CurrentlyOnUiThread
42+
{
43+
get
44+
{
45+
if (CurrentOnUiThreadDelegate == null)
46+
{
47+
return false;
48+
}
49+
50+
return CurrentOnUiThreadDelegate();
51+
}
52+
}
53+
54+
public static Task<TResult> ExecuteOnUiThread<TResult>(Func<TResult> function)
55+
{
56+
var taskFactory = UiThreadTaskFactory;
57+
58+
if (taskFactory == null)
59+
{
60+
throw new Exception("CefThread.UiThreadTaskFactory is null, ");
61+
}
62+
63+
return taskFactory.StartNew(function);
64+
}
65+
}
66+
}

CefSharp/WebBrowserExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1208,7 +1208,7 @@ private static void ThrowExceptionIfFrameNull(IFrame frame)
12081208
/// </summary>
12091209
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
12101210
/// <param name="browser">The ChromiumWebBrowser instance this method extends.</param>
1211-
private static void ThrowExceptionIfBrowserNull(this IBrowser browser)
1211+
internal static void ThrowExceptionIfBrowserNull(this IBrowser browser)
12121212
{
12131213
if (browser == null)
12141214
{

0 commit comments

Comments
 (0)