From 2e0a73e0d6adb4940f576be91fb2a17802c99a4c Mon Sep 17 00:00:00 2001 From: Per Lundberg Date: Thu, 31 Oct 2013 00:06:36 +0200 Subject: [PATCH] More heavily-WIP-style work towards #159. I wonder if we'll ever gonna get that one solved actually... Well, of course we are, but how well with it work? Anyway, time to go to bed now and so forth. --- .../CefSharp.BrowserSubprocess.csproj | 3 + CefSharp.BrowserSubprocess/JavascriptProxy.cs | 18 +++++ .../JavascriptServiceHost.cs | 51 +++++++++++++ CefSharp.BrowserSubprocess/Program.cs | 4 +- .../SubprocessCefApp.cs | 14 ++++ CefSharp/CefSharp.vcxproj | 1 + CefSharp/CefSharp.vcxproj.filters | 3 + CefSharp/Wrappers/CefAppWrapper.cpp | 2 +- CefSharp/Wrappers/CefAppWrapper.h | 33 +++++++- CefSharp/Wrappers/CefBrowserWrapper.h | 75 +++++++++++++++++++ 10 files changed, 197 insertions(+), 7 deletions(-) create mode 100644 CefSharp.BrowserSubprocess/JavascriptProxy.cs create mode 100644 CefSharp.BrowserSubprocess/JavascriptServiceHost.cs create mode 100644 CefSharp/Wrappers/CefBrowserWrapper.h diff --git a/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.csproj b/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.csproj index feb97392d1..ba872a98f0 100644 --- a/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.csproj +++ b/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.csproj @@ -70,6 +70,7 @@ + @@ -78,6 +79,8 @@ + + diff --git a/CefSharp.BrowserSubprocess/JavascriptProxy.cs b/CefSharp.BrowserSubprocess/JavascriptProxy.cs new file mode 100644 index 0000000000..27f3ab8900 --- /dev/null +++ b/CefSharp.BrowserSubprocess/JavascriptProxy.cs @@ -0,0 +1,18 @@ +using CefSharp.Internals.JavascriptBinding; +using System; + +namespace CefSharp.BrowserSubprocess +{ + internal class JavascriptProxy : IJavascriptProxy + { + public const String BaseAddress = "net.pipe://localhost"; + public const String ServiceName = "JavaScriptProxy"; + public const String Address = BaseAddress + "/" + ServiceName; + + public object EvaluateScript(int frameId, string script, double timeout) + { + var cefFrame = SubprocessCefApp.Instance.CefBrowserWrapper.GetFrame(frameId); + return null; + } + } +} \ No newline at end of file diff --git a/CefSharp.BrowserSubprocess/JavascriptServiceHost.cs b/CefSharp.BrowserSubprocess/JavascriptServiceHost.cs new file mode 100644 index 0000000000..8930eca2a6 --- /dev/null +++ b/CefSharp.BrowserSubprocess/JavascriptServiceHost.cs @@ -0,0 +1,51 @@ +using CefSharp.Internals.JavascriptBinding; +using System; +using System.ServiceModel; +using System.ServiceModel.Description; + +namespace CefSharp.BrowserSubprocess +{ + internal class JavascriptServiceHost + { + public static void Create(int browserId) + { + var uris = new[] + { + new Uri(JavascriptProxy.BaseAddress) + }; + + var host = new ServiceHost(typeof(JavascriptProxy), uris); + AddDebugBehavior(host); + + // TODO: Include the name of the "parent process" here also, so you can run more than one CefSharp-based application + // TODO: simultaneously. :) + var serviceName = JavascriptProxy.ServiceName + "_" + browserId; + + host.AddServiceEndpoint( + typeof(IJavascriptProxy), + new NetNamedPipeBinding(), + serviceName + ); + + host.Open(); + } + + private static void AddDebugBehavior(ServiceHostBase host) + { + var serviceDebugBehavior = host.Description.Behaviors.Find(); + + if (serviceDebugBehavior == null) + { + serviceDebugBehavior = new ServiceDebugBehavior + { + IncludeExceptionDetailInFaults = true + }; + host.Description.Behaviors.Add(serviceDebugBehavior); + } + else + { + serviceDebugBehavior.IncludeExceptionDetailInFaults = true; + } + } + } +} \ No newline at end of file diff --git a/CefSharp.BrowserSubprocess/Program.cs b/CefSharp.BrowserSubprocess/Program.cs index bac6820ca7..fdcabc3899 100644 --- a/CefSharp.BrowserSubprocess/Program.cs +++ b/CefSharp.BrowserSubprocess/Program.cs @@ -15,7 +15,7 @@ static int Main(string[] args) { var hInstance = Process.GetCurrentProcess().Handle; LogCommandLine(args); - //MessageBox.Show("Please attach debugger now"); + MessageBox.Show("Please attach debugger now", null, MessageBoxButtons.OK, MessageBoxIcon.Information); return ExecuteCefRenderProcess(hInstance); } @@ -27,7 +27,7 @@ private static void LogCommandLine(string[] args) private static int ExecuteCefRenderProcess(IntPtr hInstance) { - var subprocessCefApp = new SubprocessCefApp(); + var subprocessCefApp = SubprocessCefApp.Instance; return GlobalMethods.CefExecuteProcess(hInstance, subprocessCefApp); } } diff --git a/CefSharp.BrowserSubprocess/SubprocessCefApp.cs b/CefSharp.BrowserSubprocess/SubprocessCefApp.cs index b0031d46c3..9e38238d7b 100644 --- a/CefSharp.BrowserSubprocess/SubprocessCefApp.cs +++ b/CefSharp.BrowserSubprocess/SubprocessCefApp.cs @@ -4,5 +4,19 @@ namespace CefSharp.BrowserSubprocess { internal class SubprocessCefApp : CefAppWrapper { + private static SubprocessCefApp instance; + + public static SubprocessCefApp Instance + { + get { return instance ?? (instance = new SubprocessCefApp()); } + } + + public CefBrowserWrapper CefBrowserWrapper { get; private set; } + + public override void OnBrowserCreated(CefBrowserWrapper cefBrowserWrapper) + { + CefBrowserWrapper = cefBrowserWrapper; + JavascriptServiceHost.Create(cefBrowserWrapper.BrowserId); + } } } \ No newline at end of file diff --git a/CefSharp/CefSharp.vcxproj b/CefSharp/CefSharp.vcxproj index 361e48717e..5eb23949bd 100644 --- a/CefSharp/CefSharp.vcxproj +++ b/CefSharp/CefSharp.vcxproj @@ -279,6 +279,7 @@ + diff --git a/CefSharp/CefSharp.vcxproj.filters b/CefSharp/CefSharp.vcxproj.filters index 27f433f5b8..af6f45dbbf 100644 --- a/CefSharp/CefSharp.vcxproj.filters +++ b/CefSharp/CefSharp.vcxproj.filters @@ -208,5 +208,8 @@ Header Files + + Source Files\Wrappers + \ No newline at end of file diff --git a/CefSharp/Wrappers/CefAppWrapper.cpp b/CefSharp/Wrappers/CefAppWrapper.cpp index f1d989402e..887f7386e4 100644 --- a/CefSharp/Wrappers/CefAppWrapper.cpp +++ b/CefSharp/Wrappers/CefAppWrapper.cpp @@ -11,7 +11,7 @@ namespace CefSharp { CefAppWrapper::CefAppWrapper() { - cefApp = new CefRefPtr(new CefAppUnmanagedWrapper()); + cefApp = new CefRefPtr(new CefAppUnmanagedWrapper(this)); } CefAppWrapper::~CefAppWrapper() diff --git a/CefSharp/Wrappers/CefAppWrapper.h b/CefSharp/Wrappers/CefAppWrapper.h index 3425021f97..e65c4a01b7 100644 --- a/CefSharp/Wrappers/CefAppWrapper.h +++ b/CefSharp/Wrappers/CefAppWrapper.h @@ -3,16 +3,14 @@ // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. #include "Stdafx.h" +#include "CefBrowserWrapper.h" #include "include/cef_app.h" namespace CefSharp { namespace Wrappers { - private class CefAppUnmanagedWrapper : CefApp - { - IMPLEMENT_REFCOUNTING(CefAppUnmanagedWrapper); - }; + class CefAppUnmanagedWrapper; // TODO: Support the stuff that CefApp has. One important question is whether we go with the C++ API or make the (seemingly a bit // strange) move to start using the C API instead. It may actually make it possible to write our code in a cleaner fashion. Worth @@ -27,9 +25,36 @@ namespace CefSharp CefAppWrapper(); ~CefAppWrapper(); void* GetUnmanaged(); + virtual void OnBrowserCreated(CefBrowserWrapper^ cefBrowserWrapper) {}; private: !CefAppWrapper(); }; + + private class CefAppUnmanagedWrapper : CefApp, CefRenderProcessHandler + { + private: + gcroot _cefAppWrapper; + CefRefPtr _browser; + + public: + CefAppUnmanagedWrapper(CefAppWrapper^ cefAppWrapper) + { + _cefAppWrapper = cefAppWrapper; + } + + virtual DECL CefRefPtr GetRenderProcessHandler() OVERRIDE + { + return this; + } + + virtual DECL void CefAppUnmanagedWrapper::OnBrowserCreated(CefRefPtr browser) OVERRIDE + { + // TODO: Could destroy this CefBrowserWrapper in OnBrowserDestroyed(), but it doesn't seem to be reliably called... + _cefAppWrapper->OnBrowserCreated(gcnew CefBrowserWrapper(browser)); + } + + IMPLEMENT_REFCOUNTING(CefAppUnmanagedWrapper); + }; } } diff --git a/CefSharp/Wrappers/CefBrowserWrapper.h b/CefSharp/Wrappers/CefBrowserWrapper.h new file mode 100644 index 0000000000..7f8333f7c0 --- /dev/null +++ b/CefSharp/Wrappers/CefBrowserWrapper.h @@ -0,0 +1,75 @@ +// Copyright © 2010-2013 The CefSharp Project. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +#include "include/cef_browser.h" +#include "include/cef_runnable.h" + +using namespace System::Threading; + +namespace CefSharp +{ + namespace Wrappers + { + private class CefBrowserUnmanagedWrapper + { + CefRefPtr _cefBrowser; + + public: + CefRefPtr Frame; + gcroot WaitHandle; + + CefBrowserUnmanagedWrapper(CefRefPtr cefBrowser) + { + _cefBrowser = cefBrowser; + WaitHandle = gcnew AutoResetEvent(false); + } + + void GetFrameCallback(int64 frameId) + { + Frame = _cefBrowser->GetFrame(frameId); + WaitHandle->Set(); + } + + IMPLEMENT_REFCOUNTING(CefBrowserUnmanagedWrapper); + }; + + public ref class CefBrowserWrapper + { + CefRefPtr* _cefBrowser; + int _browserId; + CefRefPtr* _unmanagedWrapper; + + public: + property int BrowserId + { + int get() { return _browserId; } + private: void set(int value) { _browserId = value; } + } + + CefBrowserWrapper(CefRefPtr cefBrowser) + { + _cefBrowser = &cefBrowser; + _browserId = cefBrowser->GetIdentifier(); + + // TODO: Should be deallocated at some point to avoid leaking memory. + _unmanagedWrapper = new CefRefPtr(new CefBrowserUnmanagedWrapper(cefBrowser)); + } + + System::IntPtr GetFrame(long frameId) + { + // TODO: Could we do something genericly useful here using C++ lambdas? To avoid having to make a lot of of these... + // TODO: DON'T USE AUTORESETEVENT STUPIDITY! Even though the code below compiles & runs correctly, it deadlocks the + // thread from which the request came, which is very, very stupid, especially since V8 and Chromium are built + // with asynchrony in mind. Instead, we should re-think this API to utilize WCF callbacks instead: + // http://idunno.org/archive/2008/05/29/wcf-callbacks-a-beginners-guide.aspx + // That feels much more like 2013, and not 1994... :) + CefPostTask(CefThreadId::TID_RENDERER, NewCefRunnableMethod(_unmanagedWrapper->get(), &CefBrowserUnmanagedWrapper::GetFrameCallback, frameId)); + _unmanagedWrapper->get()->WaitHandle->WaitOne(); + + // TODO: Wrap in a CefFrameWrapper, obviously... + return (System::IntPtr)&_unmanagedWrapper->get()->Frame; + } + }; + } +}