11
11
using System . Threading . Tasks ;
12
12
using CefSharp . Callback ;
13
13
using CefSharp . Internals ;
14
+ using CefSharp . Internals . Tasks ;
14
15
15
16
namespace CefSharp . DevTools
16
17
{
@@ -19,17 +20,44 @@ namespace CefSharp.DevTools
19
20
/// </summary>
20
21
public partial class DevToolsClient : IDevToolsMessageObserver , IDevToolsClient
21
22
{
22
- private readonly ConcurrentDictionary < int , TaskCompletionSource < DevToolsMethodResponse > > queuedCommandResults = new ConcurrentDictionary < int , TaskCompletionSource < DevToolsMethodResponse > > ( ) ;
23
+ private readonly ConcurrentDictionary < int , SyncContextTaskCompletionSource < DevToolsMethodResponse > > queuedCommandResults = new ConcurrentDictionary < int , SyncContextTaskCompletionSource < DevToolsMethodResponse > > ( ) ;
23
24
private int lastMessageId ;
24
25
private IBrowser browser ;
25
26
private IRegistration devToolsRegistration ;
26
27
private bool devToolsAttached ;
28
+ private SynchronizationContext syncContext ;
27
29
28
30
/// <summary>
29
31
/// DevToolsEvent
30
32
/// </summary>
31
33
public EventHandler < DevToolsEventArgs > DevToolsEvent ;
32
34
35
+ /// <summary>
36
+ /// Capture the current <see cref="SynchronizationContext"/> so
37
+ /// continuation executes on the original calling thread. If
38
+ /// <see cref="SynchronizationContext.Current"/> is null for
39
+ /// <see cref="ExecuteDevToolsMethodAsync(string, IDictionary{string, object})"/>
40
+ /// then the continuation will be run on the CEF UI Thread (by default
41
+ /// this is not the same as the WPF/WinForms UI Thread).
42
+ /// </summary>
43
+ public bool CaptureSyncContext { get ; set ; }
44
+
45
+ /// <summary>
46
+ /// When not null provided <see cref="SynchronizationContext"/>
47
+ /// will be used to run the contination. Defaults to null
48
+ /// Setting this property will change <see cref="CaptureSyncContext"/>
49
+ /// to false.
50
+ /// </summary>
51
+ public SynchronizationContext SyncContext
52
+ {
53
+ get { return syncContext ; }
54
+ set
55
+ {
56
+ CaptureSyncContext = false ;
57
+ syncContext = value ;
58
+ }
59
+ }
60
+
33
61
/// <summary>
34
62
/// DevToolsClient
35
63
/// </summary>
@@ -39,8 +67,14 @@ public DevToolsClient(IBrowser browser)
39
67
this . browser = browser ;
40
68
41
69
lastMessageId = browser . Identifier * 100000 ;
70
+ CaptureSyncContext = true ;
42
71
}
43
72
73
+ /// <summary>
74
+ /// Store a reference to the IRegistration that's returned when
75
+ /// you register an observer.
76
+ /// </summary>
77
+ /// <param name="devToolsRegistration">registration</param>
44
78
public void SetDevToolsObserverRegistration ( IRegistration devToolsRegistration )
45
79
{
46
80
this . devToolsRegistration = devToolsRegistration ;
@@ -65,7 +99,9 @@ public async Task<DevToolsMethodResponse> ExecuteDevToolsMethodAsync(string meth
65
99
66
100
var messageId = Interlocked . Increment ( ref lastMessageId ) ;
67
101
68
- var taskCompletionSource = new TaskCompletionSource < DevToolsMethodResponse > ( ) ;
102
+ var taskCompletionSource = new SyncContextTaskCompletionSource < DevToolsMethodResponse > ( ) ;
103
+
104
+ taskCompletionSource . SyncContext = CaptureSyncContext ? SynchronizationContext . Current : syncContext ;
69
105
70
106
if ( ! queuedCommandResults . TryAdd ( messageId , taskCompletionSource ) )
71
107
{
@@ -74,16 +110,22 @@ public async Task<DevToolsMethodResponse> ExecuteDevToolsMethodAsync(string meth
74
110
75
111
var browserHost = browser . GetHost ( ) ;
76
112
113
+ //Currently on CEF UI Thread we can directly execute
77
114
if ( CefThread . CurrentlyOnUiThread )
78
115
{
79
116
var returnedMessageId = browserHost . ExecuteDevToolsMethod ( messageId , method , parameters ) ;
80
117
if ( returnedMessageId == 0 )
81
118
{
82
119
return new DevToolsMethodResponse { Success = false } ;
83
120
}
121
+ else if ( returnedMessageId != messageId )
122
+ {
123
+ //For some reason our message Id's don't match
124
+ throw new DevToolsClientException ( string . Format ( "Generated MessageId {0} doesn't match returned Message Id {1}" , returnedMessageId , messageId ) ) ;
125
+ }
84
126
}
85
-
86
- if ( CefThread . CanExecuteOnUiThread )
127
+ //Not on CEF UI Thread we need to use
128
+ else if ( CefThread . CanExecuteOnUiThread )
87
129
{
88
130
var returnedMessageId = await CefThread . ExecuteOnUiThread ( ( ) =>
89
131
{
@@ -94,6 +136,11 @@ public async Task<DevToolsMethodResponse> ExecuteDevToolsMethodAsync(string meth
94
136
{
95
137
return new DevToolsMethodResponse { Success = false } ;
96
138
}
139
+ else if ( returnedMessageId != messageId )
140
+ {
141
+ //For some reason our message Id's don't match
142
+ throw new DevToolsClientException ( string . Format ( "Generated MessageId {0} doesn't match returned Message Id {1}" , returnedMessageId , messageId ) ) ;
143
+ }
97
144
}
98
145
99
146
return await taskCompletionSource . Task ;
@@ -134,7 +181,8 @@ bool IDevToolsMessageObserver.OnDevToolsMessage(IBrowser browser, Stream message
134
181
135
182
void IDevToolsMessageObserver . OnDevToolsMethodResult ( IBrowser browser , int messageId , bool success , Stream result )
136
183
{
137
- TaskCompletionSource < DevToolsMethodResponse > taskCompletionSource = null ;
184
+ var uiThread = CefThread . CurrentlyOnUiThread ;
185
+ SyncContextTaskCompletionSource < DevToolsMethodResponse > taskCompletionSource = null ;
138
186
139
187
if ( queuedCommandResults . TryRemove ( messageId , out taskCompletionSource ) )
140
188
{
@@ -149,29 +197,41 @@ void IDevToolsMessageObserver.OnDevToolsMethodResult(IBrowser browser, int messa
149
197
150
198
result . CopyTo ( memoryStream ) ;
151
199
152
-
153
200
methodResult . ResponseAsJsonString = Encoding . UTF8 . GetString ( memoryStream . ToArray ( ) ) ;
154
201
202
+ Action execute = null ;
203
+
155
204
if ( success )
156
205
{
157
- Task . Run ( ( ) =>
206
+ execute = ( ) =>
158
207
{
159
- //Make sure continuation runs on Thread Pool
160
208
taskCompletionSource . TrySetResult ( methodResult ) ;
161
- } ) ;
209
+ } ;
162
210
}
163
211
else
164
212
{
165
- Task . Run ( ( ) =>
213
+ execute = ( ) =>
166
214
{
167
215
var errorObj = methodResult . DeserializeJson < DevToolsDomainErrorResponse > ( ) ;
168
216
errorObj . MessageId = messageId ;
169
217
170
218
//Make sure continuation runs on Thread Pool
171
219
taskCompletionSource . TrySetException ( new DevToolsClientException ( "DevTools Client Error :" + errorObj . Message , errorObj ) ) ;
172
- } ) ;
220
+ } ;
173
221
}
174
222
223
+ var syncContext = taskCompletionSource . SyncContext ;
224
+ if ( syncContext == null )
225
+ {
226
+ execute ( ) ;
227
+ }
228
+ else
229
+ {
230
+ syncContext . Post ( new SendOrPostCallback ( ( o ) =>
231
+ {
232
+ execute ( ) ;
233
+ } ) , null ) ;
234
+ }
175
235
}
176
236
}
177
237
}
0 commit comments