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; + } + }; + } +}