Skip to content

Commit bead438

Browse files
committed
Start integrating DevTools via CefSharp.Puppeteer
1 parent 5346e47 commit bead438

11 files changed

Lines changed: 229 additions & 9 deletions

CefSharp.OutOfProcess.BrowserProcess/CefSharp.OutOfProcess.BrowserProcess.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<SelfContained>false</SelfContained>
99
<ApplicationManifest>app.manifest</ApplicationManifest>
1010
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
11+
<LangVersion>Latest</LangVersion>
1112
<!--<CefSharpBuildAction>NoAction</CefSharpBuildAction>-->
1213
</PropertyGroup>
1314

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using CefSharp.Callback;
2+
using System;
3+
using System.IO;
4+
5+
namespace CefSharp.OutOfProcess.BrowserProcess
6+
{
7+
internal class CefSharpDevMessageObserver : IDevToolsMessageObserver
8+
{
9+
private Action<IBrowser, Stream> _onDevToolsMessageAction;
10+
private Action<IBrowser> _onDevtoolsAgentDetached;
11+
12+
public void Dispose()
13+
{
14+
}
15+
16+
void IDevToolsMessageObserver.OnDevToolsAgentAttached(IBrowser browser)
17+
{
18+
}
19+
20+
public CefSharpDevMessageObserver OnDevToolsAgentDetached(Action<IBrowser> action)
21+
{
22+
_onDevtoolsAgentDetached = action;
23+
24+
return this;
25+
}
26+
27+
public void OnDevToolsAgentDetached(IBrowser browser)
28+
{
29+
_onDevtoolsAgentDetached?.Invoke(browser);
30+
}
31+
32+
void IDevToolsMessageObserver.OnDevToolsEvent(IBrowser browser, string method, Stream parameters)
33+
{
34+
throw new NotImplementedException();
35+
}
36+
37+
public CefSharpDevMessageObserver OnDevToolsMessage(Action<IBrowser, Stream> action)
38+
{
39+
_onDevToolsMessageAction = action;
40+
41+
return this;
42+
}
43+
44+
bool IDevToolsMessageObserver.OnDevToolsMessage(IBrowser browser, Stream message)
45+
{
46+
_onDevToolsMessageAction?.Invoke(browser, message);
47+
48+
return true;
49+
}
50+
51+
void IDevToolsMessageObserver.OnDevToolsMethodResult(IBrowser browser, int messageId, bool success, Stream result)
52+
{
53+
throw new NotImplementedException();
54+
}
55+
}
56+
}

CefSharp.OutOfProcess.BrowserProcess/OutOfProcessChromiumWebBrowser.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
using System.Threading;
55
using System.ComponentModel;
66
using StreamJsonRpc;
7+
using System.Collections.Generic;
8+
using CefSharp.Callback;
9+
using System.IO;
710

811
namespace CefSharp.OutOfProcess.BrowserProcess
912
{
@@ -17,6 +20,9 @@ public partial class OutOfProcessChromiumWebBrowser : IWebBrowserInternal
1720
"The undelying CefBrowser instance is not yet initialized. Use the IsBrowserInitializedChanged event and check " +
1821
"the IsBrowserInitialized property to determine when the browser has been initialized.";
1922

23+
private IDevToolsMessageObserver _devtoolsMessageObserver;
24+
private IRegistration _devtoolsRegistration;
25+
2026
/// <summary>
2127
/// Internal ID used for tracking browsers between Processes;
2228
/// </summary>
@@ -343,7 +349,44 @@ void IWebBrowserInternal.OnAfterBrowserCreated(IBrowser browser)
343349
initialLoadAction = InitialLoad;
344350
Interlocked.Exchange(ref browserInitialized, 1);
345351

352+
var host = browser.GetHost();
353+
346354
_ = _jsonRpc.NotifyAsync("OnAfterBrowserCreated", _id, browser.GetHost().GetWindowHandle().ToInt32());
355+
356+
var observer = new CefSharpDevMessageObserver();
357+
observer.OnDevToolsAgentDetached((b) =>
358+
{
359+
_ = _jsonRpc.NotifyAsync("OnDevToolsAgentDetached", _id);
360+
});
361+
observer.OnDevToolsMessage((b, m) =>
362+
{
363+
using var reader = new StreamReader(m);
364+
var msg = reader.ReadToEnd();
365+
366+
_ = _jsonRpc.NotifyAsync("OnDevToolsMessage", _id, msg);
367+
});
368+
369+
_devtoolsMessageObserver = observer;
370+
371+
_devtoolsRegistration = host.AddDevToolsMessageObserver(_devtoolsMessageObserver);
372+
373+
var devToolsClient = browser.GetDevToolsClient();
374+
375+
//TODO: Do we need perforamnce and Log enabled?
376+
var devToolsEnableTask = Task.WhenAll(devToolsClient.Page.EnableAsync(),
377+
devToolsClient.Page.SetLifecycleEventsEnabledAsync(true),
378+
devToolsClient.Runtime.EnableAsync(),
379+
devToolsClient.Network.EnableAsync(),
380+
devToolsClient.Performance.EnableAsync(),
381+
devToolsClient.Log.EnableAsync());
382+
383+
_ = devToolsEnableTask.ContinueWith(t =>
384+
{
385+
((IDisposable)devToolsClient).Dispose();
386+
387+
_ = _jsonRpc.NotifyAsync("OnDevToolsReady", _id);
388+
389+
}, TaskScheduler.Default);
347390
}
348391

349392
/// <summary>
@@ -672,6 +715,11 @@ protected virtual void Dispose(bool disposing)
672715

673716
if (disposing)
674717
{
718+
_devtoolsRegistration?.Dispose();
719+
_devtoolsRegistration = null;
720+
_devtoolsMessageObserver?.Dispose();
721+
_devtoolsMessageObserver = null;
722+
675723
CanExecuteJavascriptInMainFrame = false;
676724
Interlocked.Exchange(ref browserInitialized, 0);
677725

CefSharp.OutOfProcess.Core/CefSharp.OutOfProcess.Core.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
<PackageReference Include="PInvoke.Kernel32" Version="0.7.104" />
1010
<PackageReference Include="PInvoke.User32" Version="0.7.104" />
1111
<PackageReference Include="StreamJsonRpc" Version="2.11.35" />
12+
13+
<ProjectReference Include="..\CefSharp.Puppeteer\lib\PuppeteerSharp\CefSharp.DevTools.OutOfProcess.csproj" />
1214
</ItemGroup>
1315

1416
</Project>

CefSharp.OutOfProcess.Core/IChromiumWebBrowser.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,23 @@ public interface IChromiumWebBrowser
1010
/// <summary>
1111
/// Identifier
1212
/// </summary>
13-
int Id { get; set; }
13+
int Id { get; }
1414

1515
/// <summary>
1616
/// Set the browser Hwnd
1717
/// </summary>
1818
/// <param name="hwnd">Hwnd</param>
1919
void SetBrowserHwnd(IntPtr hwnd);
20+
21+
/// <summary>
22+
/// Called when a DevTools message arrives from the browser process
23+
/// </summary>
24+
/// <param name="jsonMsg"></param>
25+
void OnDevToolsMessage(string jsonMsg);
26+
27+
/// <summary>
28+
/// DevTools is ready in the browser process to create the DevToolsContext
29+
/// </summary>
30+
void OnDevToolsReady();
2031
}
2132
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using CefSharp.Puppeteer.Transport;
2+
using System;
3+
using System.Threading.Tasks;
4+
5+
namespace CefSharp.OutOfProcess.WinForms
6+
{
7+
public class OutOfProcessConnectionTransport : IConnectionTransport
8+
{
9+
public int BrowserId { get; }
10+
public bool IsClosed { get; private set; }
11+
public OutOfProcessHost OutOfProcessHost { get; }
12+
13+
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
14+
15+
public OutOfProcessConnectionTransport(int browserId, OutOfProcessHost outOfProcessHost)
16+
{
17+
BrowserId = browserId;
18+
OutOfProcessHost = outOfProcessHost;
19+
}
20+
21+
void IDisposable.Dispose()
22+
{
23+
24+
}
25+
26+
public void InvokeMessageReceived(string message)
27+
{
28+
MessageReceived?.Invoke(this, new MessageReceivedEventArgs(message));
29+
}
30+
31+
public Task SendAsync(string message)
32+
{
33+
return OutOfProcessHost.SendDevToolsMessage(BrowserId, message);
34+
}
35+
36+
public void StopReading()
37+
{
38+
39+
}
40+
}
41+
}

CefSharp.OutOfProcess.Core/OutOfProcessHost.cs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,27 @@ public class OutOfProcessHost : IDisposable
2222
private int _browserIdentifier = 1;
2323
private string _outofProcessFilePath;
2424
private ConcurrentDictionary<int, IChromiumWebBrowser> _browsers = new ConcurrentDictionary<int, IChromiumWebBrowser>();
25+
2526
private TaskCompletionSource<OutOfProcessHost> _processInitialized = new TaskCompletionSource<OutOfProcessHost>(TaskCreationOptions.RunContinuationsAsynchronously);
2627

2728
private OutOfProcessHost(string path)
2829
{
2930
_outofProcessFilePath = path;
3031
}
3132

32-
public bool CreateBrowser(IChromiumWebBrowser browser, IntPtr handle, string url)
33+
public bool CreateBrowser(IChromiumWebBrowser browser, IntPtr handle, string url, out int id)
3334
{
34-
var id = _browserIdentifier++;
35+
id = _browserIdentifier++;
3536
_ = _jsonRpc.NotifyAsync("CreateBrowser", handle.ToInt32(), url, id);
3637

37-
browser.Id = id;
38-
3938
return _browsers.TryAdd(id, browser);
4039
}
4140

41+
internal Task SendDevToolsMessage(int browserId, string message)
42+
{
43+
return _jsonRpc.NotifyAsync("SendDevToolsMessage", browserId, message);
44+
}
45+
4246
private Task<OutOfProcessHost> InitializedTask
4347
{
4448
get { return _processInitialized.Task; }
@@ -80,6 +84,30 @@ private void Init()
8084
//var attached = User32.AttachThreadInput(_remoteThreadId, _uiThreadId, true);
8185
});
8286

87+
_jsonRpc.AddLocalRpcMethod("OnDevToolsMessage", (Action<int, string>)delegate (int browserId, string jsonMsg)
88+
{
89+
if (_browsers.TryGetValue(browserId, out var chromiumWebBrowser))
90+
{
91+
chromiumWebBrowser.OnDevToolsMessage(jsonMsg);
92+
}
93+
});
94+
95+
_jsonRpc.AddLocalRpcMethod("OnDevToolsAgentDetached", (Action<int>)delegate (int browserId)
96+
{
97+
if (_browsers.TryGetValue(browserId, out var chromiumWebBrowser))
98+
{
99+
100+
}
101+
});
102+
103+
_jsonRpc.AddLocalRpcMethod("OnDevToolsReady", (Action<int>)delegate (int browserId)
104+
{
105+
if (_browsers.TryGetValue(browserId, out var chromiumWebBrowser))
106+
{
107+
chromiumWebBrowser.OnDevToolsReady();
108+
}
109+
});
110+
83111
_jsonRpc.AllowModificationWhileListening = false;
84112
}
85113

CefSharp.OutOfProcess.WinForms/CefSharp.OutOfProcess.WinForms.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
<ItemGroup>
1515
<ProjectReference Include="..\CefSharp.OutOfProcess.Core\CefSharp.OutOfProcess.Core.csproj" />
16+
<ProjectReference Include="..\CefSharp.Puppeteer\lib\PuppeteerSharp\CefSharp.DevTools.OutOfProcess.csproj" />
1617
</ItemGroup>
1718

1819
<ItemGroup>

CefSharp.OutOfProcess.WinForms/ChromiumWebBrowser.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Drawing;
33
using System.Windows.Forms;
44
using CefSharp.OutOfProcess;
5+
using CefSharp.Puppeteer;
56
using PInvoke;
67

78
namespace CefSharp.OutOfProcess.WinForms
@@ -15,11 +16,11 @@ public class ChromiumWebBrowser : Control, IChromiumWebBrowser
1516
private OutOfProcessHost _host;
1617
private readonly string _initialAddress;
1718
private int _id;
19+
private IDevToolsContext _devToolsContext;
1820

1921
int IChromiumWebBrowser.Id
2022
{
2123
get { return _id; }
22-
set { _id = value; }
2324
}
2425

2526
public ChromiumWebBrowser(OutOfProcessHost host, string initialAddress)
@@ -48,7 +49,10 @@ protected override void OnHandleCreated(EventArgs e)
4849
{
4950
base.OnHandleCreated(e);
5051

51-
_host.CreateBrowser(this, Handle, url: _initialAddress);
52+
_host.CreateBrowser(this, Handle, url: _initialAddress, out _id);
53+
54+
var connection = Connection.Attach(new OutOfProcessConnectionTransport(_id, _host));
55+
_devToolsContext = DevToolsContext.CreateForOutOfProcess(connection);
5256
}
5357

5458
protected override void Dispose(bool disposing)
@@ -141,5 +145,27 @@ internal virtual void ShowInternal(int width, int height)
141145
User32.SetWindowPos(_browserHwnd, IntPtr.Zero, 0, 0, width, height, User32.SetWindowPosFlags.SWP_NOZORDER);
142146
}
143147
}
148+
149+
public void OnDevToolsMessage(string jsonMsg)
150+
{
151+
if(_devToolsContext != null)
152+
{
153+
//TODO: This is messy, cleanup the ownership to improve this
154+
_devToolsContext = (DevToolsContext)_devToolsContext;
155+
var transport = _devToolsContext.Client.Transport as OutOfProcessConnectionTransport;
156+
157+
transport.InvokeMessageReceived(jsonMsg);
158+
}
159+
}
160+
161+
public void OnDevToolsReady()
162+
{
163+
var ctx = (DevToolsContext)_devToolsContext;
164+
165+
_ = ctx.InvokeGetFrameTreeAsync().ContinueWith(t =>
166+
{
167+
//NOW the user can start using the devtools context
168+
});
169+
}
144170
}
145171
}

CefSharp.OutOfProcess.sln

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.OutOfProcess.Brows
99
EndProject
1010
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.OutOfProcess.WinForms", "CefSharp.OutOfProcess.WinForms\CefSharp.OutOfProcess.WinForms.csproj", "{8287C019-2720-47D5-8077-1A2DADB510E6}"
1111
EndProject
12-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CefSharp.OutOfProcess.Core", "CefSharp.OutOfProcess.Core\CefSharp.OutOfProcess.Core.csproj", "{6ED4E6BF-F43A-429C-87FD-E66203EEC348}"
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.OutOfProcess.Core", "CefSharp.OutOfProcess.Core\CefSharp.OutOfProcess.Core.csproj", "{6ED4E6BF-F43A-429C-87FD-E66203EEC348}"
13+
EndProject
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.DevTools.OutOfProcess", "CefSharp.Puppeteer\lib\PuppeteerSharp\CefSharp.DevTools.OutOfProcess.csproj", "{821ED4FE-5661-45D8-9523-44BDFB9B5DB6}"
1315
EndProject
1416
Global
1517
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -33,6 +35,10 @@ Global
3335
{6ED4E6BF-F43A-429C-87FD-E66203EEC348}.Debug|Any CPU.Build.0 = Debug|Any CPU
3436
{6ED4E6BF-F43A-429C-87FD-E66203EEC348}.Release|Any CPU.ActiveCfg = Release|Any CPU
3537
{6ED4E6BF-F43A-429C-87FD-E66203EEC348}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{821ED4FE-5661-45D8-9523-44BDFB9B5DB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{821ED4FE-5661-45D8-9523-44BDFB9B5DB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{821ED4FE-5661-45D8-9523-44BDFB9B5DB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{821ED4FE-5661-45D8-9523-44BDFB9B5DB6}.Release|Any CPU.Build.0 = Release|Any CPU
3642
EndGlobalSection
3743
GlobalSection(SolutionProperties) = preSolution
3844
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)