From cc8a07c21f71b23d8129f31d8debed1458b3322a Mon Sep 17 00:00:00 2001 From: David Serfozo Date: Sat, 27 Jun 2015 18:24:32 +0200 Subject: [PATCH] added evaluatescriptasync message handlers and rerouted evalscript to cef ipc --- .../CefAppUnmanagedWrapper.cpp | 7 +- .../CefAppUnmanagedWrapper.h | 8 ++ .../CefBrowserWrapper.cpp | 10 ++ .../CefBrowserWrapper.h | 9 ++ .../CefSharp.BrowserSubprocess.Core.vcxproj | 2 + ...arp.BrowserSubprocess.Core.vcxproj.filters | 6 + .../Messaging/EvaluateScriptDelegate.cpp | 112 ++++++++++++++++++ .../Messaging/EvaluateScriptDelegate.h | 34 ++++++ CefSharp.Core/CefSharp.Core.vcxproj | 2 + CefSharp.Core/CefSharp.Core.vcxproj.filters | 6 + CefSharp.Core/Internals/ClientAdapter.cpp | 10 ++ CefSharp.Core/Internals/ClientAdapter.h | 15 ++- .../Messaging/EvaluateScriptDoneDelegate.cpp | 90 ++++++++++++++ .../Messaging/EvaluateScriptDoneDelegate.h | 32 +++++ CefSharp.Core/ManagedCefBrowserAdapter.cpp | 18 +-- CefSharp/CefSharp.csproj | 1 + CefSharp/Internals/PendingTaskRepository.cs | 78 ++++++++++++ 17 files changed, 423 insertions(+), 17 deletions(-) create mode 100644 CefSharp.BrowserSubprocess.Core/Messaging/EvaluateScriptDelegate.cpp create mode 100644 CefSharp.BrowserSubprocess.Core/Messaging/EvaluateScriptDelegate.h create mode 100644 CefSharp.Core/Internals/Messaging/EvaluateScriptDoneDelegate.cpp create mode 100644 CefSharp.Core/Internals/Messaging/EvaluateScriptDoneDelegate.h create mode 100644 CefSharp/Internals/PendingTaskRepository.cs diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp index cf88a23de9..6598ba493b 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp @@ -71,6 +71,11 @@ namespace CefSharp CefBrowserWrapper^ CefAppUnmanagedWrapper::FindBrowserWrapper(CefRefPtr browser, bool mustExist) { auto browserId = browser->GetIdentifier(); + return FindBrowserWrapper(browserId, mustExist); + }; + + CefBrowserWrapper^ CefAppUnmanagedWrapper::FindBrowserWrapper(int browserId, bool mustExist) + { CefBrowserWrapper^ wrapper = nullptr; _browserWrappers->TryGetValue(browserId, wrapper); @@ -81,7 +86,7 @@ namespace CefSharp } return wrapper; - }; + } bool CefAppUnmanagedWrapper::OnProcessMessageReceived(CefRefPtr browser, CefProcessId source_process, CefRefPtr message) { diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h index e83c91a840..c0c4100def 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h @@ -10,6 +10,7 @@ #include "include/cef_base.h" #include "CefBrowserWrapper.h" +#include "Messaging/EvaluateScriptDelegate.h" #include "../CefSharp.Core/Internals/Messaging/ProcessMessageDelegate.h" using namespace System::Collections::Generic; @@ -22,11 +23,15 @@ namespace CefSharp private class CefAppUnmanagedWrapper : CefApp, CefRenderProcessHandler { private: + friend EvaluateScriptDelegate; + ProcessMessageDelegateSet _processMessageDelegates; gcroot^> _onBrowserCreated; gcroot^> _onBrowserDestroyed; gcroot^> _browserWrappers; + + CefBrowserWrapper^ FindBrowserWrapper(int browserId, bool mustExist); CefBrowserWrapper^ FindBrowserWrapper(CefRefPtr browser, bool mustExist); public: @@ -35,6 +40,9 @@ namespace CefSharp _onBrowserCreated = onBrowserCreated; _onBrowserDestroyed = onBrowserDestoryed; _browserWrappers = gcnew Dictionary(); + + //Register evaluate script request handler + AddProcessMessageDelegate(new EvaluateScriptDelegate(this)); } ~CefAppUnmanagedWrapper() diff --git a/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.cpp b/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.cpp index 7f415274fe..04fb9316d8 100644 --- a/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.cpp @@ -87,6 +87,16 @@ namespace CefSharp _callbackRegistry->Deregister(id); } + CefRefPtr CefBrowserWrapper::GetWrapped() + { + return _cefBrowser.get(); + } + + JavascriptCallbackRegistry^ CefBrowserWrapper::CallbackRegistry::get() + { + return _callbackRegistry; + } + CefBrowserWrapper::!CefBrowserWrapper() { _cefBrowser = nullptr; diff --git a/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h b/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h index 7c5b85a9a9..af95ad8b42 100644 --- a/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h @@ -30,6 +30,15 @@ namespace CefSharp JavascriptCallbackRegistry^ _callbackRegistry; JavascriptRootObjectWrapper^ _javascriptRootObjectWrapper; + internal: + //used by EvaluateScriptDelegate + CefRefPtr GetWrapped(); + + property JavascriptCallbackRegistry^ CallbackRegistry + { + CefSharp::Internals::JavascriptCallbackRegistry^ get(); + } + public: CefBrowserWrapper(CefRefPtr cefBrowser); !CefBrowserWrapper(); diff --git a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj index 3c11f2d64c..35bfc0f7d8 100644 --- a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj +++ b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj @@ -162,6 +162,7 @@ + @@ -182,6 +183,7 @@ + diff --git a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj.filters b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj.filters index 5c744c4a0f..0958c2a4e3 100644 --- a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj.filters +++ b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj.filters @@ -74,6 +74,9 @@ Header Files + + Header Files + @@ -124,6 +127,9 @@ Source Files + + Source Files + diff --git a/CefSharp.BrowserSubprocess.Core/Messaging/EvaluateScriptDelegate.cpp b/CefSharp.BrowserSubprocess.Core/Messaging/EvaluateScriptDelegate.cpp new file mode 100644 index 0000000000..95a0a9276b --- /dev/null +++ b/CefSharp.BrowserSubprocess.Core/Messaging/EvaluateScriptDelegate.cpp @@ -0,0 +1,112 @@ +// Copyright © 2010-2015 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 "stdafx.h" +#include "../CefSharp.Core/Internals/Messaging/Messages.h" +#include "../CefSharp.Core/Internals/Serialization/Primitives.h" + +#include "Serialization/V8Serialization.h" +#include "../CefAppUnmanagedWrapper.h" +#include "EvaluateScriptDelegate.h" + +namespace CefSharp +{ + namespace Internals + { + using namespace Serialization; + + namespace Messaging + { + EvaluateScriptDelegate::EvaluateScriptDelegate(CefRefPtr appUnmanagedWrapper) + :_appUnmanagedWrapper(appUnmanagedWrapper) + { + + } + + bool EvaluateScriptDelegate::OnProcessMessageReceived(CefRefPtr browser, CefProcessId source_process, CefRefPtr message) + { + auto handled = false; + auto name = message->GetName(); + if (name == kEvaluateJavascript) + { + auto argList = message->GetArgumentList(); + auto browserId = argList->GetInt(0); + auto frameId = argList->GetInt(1); + auto callbackId = GetInt64(argList, 2); + auto script = argList->GetString(3); + + auto response = EvaluateScript(browserId, frameId, callbackId, script); + if (response.get()) + { + browser->SendProcessMessage(source_process, response); + } + + handled = true; + } + + return handled; + } + + CefRefPtr EvaluateScriptDelegate::EvaluateScript(int browserId, int frameId, int64 callbackId, CefString script) + { + CefRefPtr result; + auto browserWrapper = _appUnmanagedWrapper->FindBrowserWrapper(browserId, true); + auto browser = browserWrapper->GetWrapped(); + auto frame = browser->GetFrame(frameId); + if (browser.get() && frame.get()) + { + result = EvaluateScriptInFrame(frame, callbackId, script, browserWrapper->CallbackRegistry); + } + else + { + //TODO handle error + } + return result; + } + + CefRefPtr EvaluateScriptDelegate::EvaluateScriptInFrame(CefRefPtr frame, int64 callbackId, CefString script, JavascriptCallbackRegistry^ callbackRegistry) + { + CefRefPtr result; + auto context = frame->GetV8Context(); + + if (context.get() && context->Enter()) + { + try + { + result = EvaluateScriptInContext(context, callbackId, script, callbackRegistry); + } + finally + { + context->Exit(); + } + } + + return result; + } + + CefRefPtr EvaluateScriptDelegate::EvaluateScriptInContext(CefRefPtr context, int64 callbackId, CefString script, JavascriptCallbackRegistry^ callbackRegistry) + { + CefRefPtr result; + CefRefPtr exception; + auto success = context->Eval(script, result, exception); + auto response = CefProcessMessage::Create(kEvaluateJavascriptDone); + auto argList = response->GetArgumentList(); + + argList->SetBool(0, success); + SetInt64(callbackId, argList, 1); + if (success) + { + SerializeV8Object(result, argList, 2, callbackRegistry); + } + else + { + argList->SetString(2, exception->GetMessage()); + } + + return response; + } + + } + } +} \ No newline at end of file diff --git a/CefSharp.BrowserSubprocess.Core/Messaging/EvaluateScriptDelegate.h b/CefSharp.BrowserSubprocess.Core/Messaging/EvaluateScriptDelegate.h new file mode 100644 index 0000000000..fd38919519 --- /dev/null +++ b/CefSharp.BrowserSubprocess.Core/Messaging/EvaluateScriptDelegate.h @@ -0,0 +1,34 @@ +// Copyright © 2010-2015 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. + +#pragma once + +#include "../CefSharp.Core/Internals/Messaging/ProcessMessageDelegate.h" + +namespace CefSharp +{ + class CefAppUnmanagedWrapper; + + namespace Internals + { + namespace Messaging + { + //This class handles incoming evaluate script messages and responses to them after fulfillment. + class EvaluateScriptDelegate : public ProcessMessageDelegate + { + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(EvaluateScriptDelegate); + + CefRefPtr _appUnmanagedWrapper; + + CefRefPtr EvaluateScript(int browserId, int frameId, int64 callbackId, CefString script); + CefRefPtr EvaluateScriptInFrame(CefRefPtr frame, int64 callbackId, CefString script, JavascriptCallbackRegistry^ callbackRegistry); + CefRefPtr EvaluateScriptInContext(CefRefPtr context, int64 callbackId, CefString script, JavascriptCallbackRegistry^ callbackRegistry); + public: + EvaluateScriptDelegate(CefRefPtr appUnmanagedWrapper); + virtual bool OnProcessMessageReceived(CefRefPtr browser, CefProcessId source_process, CefRefPtr message) override; + }; + } + } +} \ No newline at end of file diff --git a/CefSharp.Core/CefSharp.Core.vcxproj b/CefSharp.Core/CefSharp.Core.vcxproj index 03df538bb2..d48771ed26 100644 --- a/CefSharp.Core/CefSharp.Core.vcxproj +++ b/CefSharp.Core/CefSharp.Core.vcxproj @@ -219,6 +219,7 @@ + @@ -245,6 +246,7 @@ + diff --git a/CefSharp.Core/CefSharp.Core.vcxproj.filters b/CefSharp.Core/CefSharp.Core.vcxproj.filters index ff4ad11d08..5348e7be72 100644 --- a/CefSharp.Core/CefSharp.Core.vcxproj.filters +++ b/CefSharp.Core/CefSharp.Core.vcxproj.filters @@ -65,6 +65,9 @@ Source Files + + Source Files + @@ -199,6 +202,9 @@ Header Files + + Header Files + diff --git a/CefSharp.Core/Internals/ClientAdapter.cpp b/CefSharp.Core/Internals/ClientAdapter.cpp index 379c27a21e..4e49c9e878 100644 --- a/CefSharp.Core/Internals/ClientAdapter.cpp +++ b/CefSharp.Core/Internals/ClientAdapter.cpp @@ -812,6 +812,16 @@ namespace CefSharp return handled; } + Task^ ClientAdapter::EvaluateScriptAsync(int browserId, int frameId, String^ script, Nullable timeout) + { + Task^ result = nullptr; + if (_cefBrowser.get()) + { + result = _evalScriptDoneDelegate->EvaluateScriptAsync(_cefBrowser, browserId, frameId, script, timeout); + } + return result; + } + void ClientAdapter::AddProcessMessageDelegate(CefRefPtr processMessageDelegate) { _processMessageDelegates.insert(processMessageDelegate); diff --git a/CefSharp.Core/Internals/ClientAdapter.h b/CefSharp.Core/Internals/ClientAdapter.h index d9bbdd11ad..5b9f1667c4 100644 --- a/CefSharp.Core/Internals/ClientAdapter.h +++ b/CefSharp.Core/Internals/ClientAdapter.h @@ -11,6 +11,7 @@ #include "include/cef_render_process_handler.h" #include "include/internal/cef_types.h" #include "Internals/Messaging/ProcessMessageDelegate.h" +#include "Internals/Messaging/EvaluateScriptDoneDelegate.h" using namespace System; @@ -38,10 +39,16 @@ namespace CefSharp gcroot _browserControl; HWND _browserHwnd; CefRefPtr _cefBrowser; + //eval script handler + CefRefPtr _evalScriptDoneDelegate; + ProcessMessageDelegateSet _processMessageDelegates; gcroot^> _popupBrowsers; gcroot _tooltip; gcroot _browserAdapter; + //contains in-progress eval script tasks + gcroot^> _pendingTaskRepository; + void ThrowUnknownPopupBrowser(String^ context) { @@ -54,8 +61,12 @@ namespace CefSharp ClientAdapter(IWebBrowserInternal^ browserControl, IBrowserAdapter^ browserAdapter) : _browserControl(browserControl), _popupBrowsers(gcnew Dictionary()), + _pendingTaskRepository(gcnew PendingTaskRepository()), _browserAdapter(browserAdapter) { + //create eval script message handler + _evalScriptDoneDelegate = new EvaluateScriptDoneDelegate(_pendingTaskRepository); + AddProcessMessageDelegate(_evalScriptDoneDelegate); } ~ClientAdapter() @@ -164,8 +175,10 @@ namespace CefSharp virtual void OnDownloadUpdated(CefRefPtr browser, CefRefPtr download_item, CefRefPtr callback) OVERRIDE; - void AddProcessMessageDelegate(CefRefPtr processMessageDelegate); + //sends out an eval script request to the render process + Task^ EvaluateScriptAsync(int browserId, int frameId, String^ script, Nullable timeout); + void AddProcessMessageDelegate(CefRefPtr processMessageDelegate); IMPLEMENT_REFCOUNTING(ClientAdapter); }; diff --git a/CefSharp.Core/Internals/Messaging/EvaluateScriptDoneDelegate.cpp b/CefSharp.Core/Internals/Messaging/EvaluateScriptDoneDelegate.cpp new file mode 100644 index 0000000000..35f25bf901 --- /dev/null +++ b/CefSharp.Core/Internals/Messaging/EvaluateScriptDoneDelegate.cpp @@ -0,0 +1,90 @@ +// Copyright © 2010-2015 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +#include "Stdafx.h" +#include "Messages.h" +#include "../Serialization/Primitives.h" +#include "../Serialization/V8Serialization.h" +#include "EvaluateScriptDoneDelegate.h" + +namespace CefSharp +{ + namespace Internals + { + using namespace Serialization; + + namespace Messaging + { + EvaluateScriptDoneDelegate::EvaluateScriptDoneDelegate(PendingTaskRepository^ pendingTasks) + :_pendingTasks(pendingTasks) + { + + } + + Task^ EvaluateScriptDoneDelegate::EvaluateScriptAsync(CefRefPtr cefBrowser, int browserId, int frameId, String^ script, Nullable timeout) + { + TaskCompletionSource^ completionSource = nullptr; + //create a new taskcompletionsource based on wether we do or do not have a timeout + auto callbackId = timeout.HasValue ? _pendingTasks->CreatePendingTaskWithTimeout(completionSource, timeout.Value) : + _pendingTasks->CreatePendingTask(completionSource); + + auto message = CefProcessMessage::Create(kEvaluateJavascript); + auto argList = message->GetArgumentList(); + argList->SetInt(0, browserId); + argList->SetInt(1, frameId); + SetInt64(callbackId, argList, 2); + argList->SetString(3, StringUtils::ToNative(script)); + + cefBrowser->SendProcessMessage(CefProcessId::PID_RENDERER, message); + + return completionSource->Task; + } + + bool EvaluateScriptDoneDelegate::OnProcessMessageReceived(CefRefPtr browser, CefProcessId source_process, CefRefPtr message) + { + auto handled = false; + auto name = message->GetName(); + if (name == kEvaluateJavascriptDone) + { + auto argList = message->GetArgumentList(); + auto success = argList->GetBool(0); + auto callbackId = GetInt64(argList, 1); + + FinishTask(callbackId, success, argList); + + handled = true; + } + + return handled; + } + + void EvaluateScriptDoneDelegate::FinishTask(int64 callbackId, bool success, CefRefPtr message) + { + auto pendingTask = _pendingTasks->RemovePendingTask(callbackId); + if (pendingTask != nullptr) + { + pendingTask->SetResult(CreateResponse(success, message)); + } + } + + JavascriptResponse^ EvaluateScriptDoneDelegate::CreateResponse(bool success, CefRefPtr message) + { + auto result = gcnew JavascriptResponse(); + result->Success = success; + + if (success) + { + //pass null as IJavascriptCallbackFactory for now + result->Result = DeserializeV8Object(message, 2, nullptr); + } + else + { + result->Message = StringUtils::ToClr(message->GetString(2)); + } + + return result; + } + } + } +} \ No newline at end of file diff --git a/CefSharp.Core/Internals/Messaging/EvaluateScriptDoneDelegate.h b/CefSharp.Core/Internals/Messaging/EvaluateScriptDoneDelegate.h new file mode 100644 index 0000000000..e9b3f2efc8 --- /dev/null +++ b/CefSharp.Core/Internals/Messaging/EvaluateScriptDoneDelegate.h @@ -0,0 +1,32 @@ +// Copyright © 2010-2015 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +#pragma once + +#include "ProcessMessageDelegate.h" + +namespace CefSharp +{ + namespace Internals + { + namespace Messaging + { + //This class will handle result messages of eval script calls + class EvaluateScriptDoneDelegate : public ProcessMessageDelegate + { + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(EvaluateScriptDoneDelegate); + + gcroot^> _pendingTasks; + + void FinishTask(int64 callbackId, bool success, CefRefPtr message); + JavascriptResponse^ CreateResponse(bool success, CefRefPtr message); + public: + EvaluateScriptDoneDelegate(PendingTaskRepository^ pendingTasks); + Task^ EvaluateScriptAsync(CefRefPtr cefBrowser, int browserId, int frameId, String^ script, Nullable timeout); + virtual bool OnProcessMessageReceived(CefRefPtr browser, CefProcessId source_process, CefRefPtr message) override; + }; + } + } +} \ No newline at end of file diff --git a/CefSharp.Core/ManagedCefBrowserAdapter.cpp b/CefSharp.Core/ManagedCefBrowserAdapter.cpp index bcc744df24..0f0058f503 100644 --- a/CefSharp.Core/ManagedCefBrowserAdapter.cpp +++ b/CefSharp.Core/ManagedCefBrowserAdapter.cpp @@ -39,8 +39,6 @@ void ManagedCefBrowserAdapter::LoadUrl(String^ address) void ManagedCefBrowserAdapter::OnAfterBrowserCreated(int browserId) { - _wcfEnabled = Cef::WcfEnabled; - if (_wcfEnabled) { _browserProcessServiceHost = gcnew BrowserProcessServiceHost(_javaScriptObjectRepository, Process::GetCurrentProcess()->Id, browserId); @@ -291,12 +289,7 @@ Task^ ManagedCefBrowserAdapter::EvaluateScriptAsync(int bro throw gcnew ArgumentOutOfRangeException("timeout", "Timeout greater than Maximum allowable value of " + UInt32::MaxValue); } - if (!_wcfEnabled) - { - throw gcnew InvalidOperationException("To wait for javascript code set WcfEnabled true in CefSettings during initialization."); - } - - return _browserProcessServiceHost->EvaluateScriptAsync(browserId, frameId, script, timeout); + return _clientAdapter->EvaluateScriptAsync(browserId, frameId, script, timeout); } Task^ ManagedCefBrowserAdapter::EvaluateScriptAsync(String^ script, Nullable timeout) @@ -306,14 +299,9 @@ Task^ ManagedCefBrowserAdapter::EvaluateScriptAsync(String^ throw gcnew ArgumentOutOfRangeException("timeout", "Timeout greater than Maximum allowable value of " + UInt32::MaxValue); } - if (!_wcfEnabled) - { - throw gcnew InvalidOperationException("To wait for javascript code set WcfEnabled true in CefSettings during initialization."); - } - auto browser = _clientAdapter->GetCefBrowser(); - if (_browserProcessServiceHost == nullptr || browser == nullptr) + if (browser == nullptr) { return nullptr; } @@ -325,7 +313,7 @@ Task^ ManagedCefBrowserAdapter::EvaluateScriptAsync(String^ return nullptr; } - return _browserProcessServiceHost->EvaluateScriptAsync(browser->GetIdentifier(), frame->GetIdentifier(), script, timeout); + return _clientAdapter->EvaluateScriptAsync(browser->GetIdentifier(), frame->GetIdentifier(), script, timeout); } void ManagedCefBrowserAdapter::CreateBrowser(BrowserSettings^ browserSettings, IntPtr sourceHandle, String^ address) diff --git a/CefSharp/CefSharp.csproj b/CefSharp/CefSharp.csproj index b0b6140a44..a175ee06b6 100644 --- a/CefSharp/CefSharp.csproj +++ b/CefSharp/CefSharp.csproj @@ -118,6 +118,7 @@ + diff --git a/CefSharp/Internals/PendingTaskRepository.cs b/CefSharp/Internals/PendingTaskRepository.cs new file mode 100644 index 0000000000..a15be32330 --- /dev/null +++ b/CefSharp/Internals/PendingTaskRepository.cs @@ -0,0 +1,78 @@ +// Copyright © 2010-2015 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. + +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace CefSharp.Internals.Messaging +{ + /// + /// Class to store TaskCompletionSources indexed by a unique id. + /// + /// The type of the result produced by the tasks held. + public sealed class PendingTaskRepository + { + private readonly ConcurrentDictionary> pendingTasks = + new ConcurrentDictionary>(); + //should only be accessed by Interlocked.Increment + private long lastId = 0; + + /// + /// Creates a new pending task. + /// + /// The newly created + /// The unique id of the newly created pending task. + public long CreatePendingTask(out TaskCompletionSource completionSource) + { + completionSource = new TaskCompletionSource(); + return SaveCompletionSource(completionSource); + } + + /// + /// Creates a new pending task with a timeout. + /// + /// The maximum running time of the task. + /// The newly created + /// The unique id of the newly created pending task. + public long CreatePendingTaskWithTimeout(out TaskCompletionSource completionSource, TimeSpan timeout) + { + var taskCompletionSource = new TaskCompletionSource(); + completionSource = taskCompletionSource; + + var id = SaveCompletionSource(completionSource); + Timer timer = null; + timer = new Timer(state => + { + timer.Dispose(); + RemovePendingTask(id); + taskCompletionSource.TrySetCanceled(); + }, null, timeout, TimeSpan.FromMilliseconds(-1)); + + return id; + } + + /// + /// Gets and removed pending task by id. + /// + /// Unique id of the pending task. + /// + /// The associated with the given id. + /// + public TaskCompletionSource RemovePendingTask(long id) + { + TaskCompletionSource result; + pendingTasks.TryRemove(id, out result); + return result; + } + + private long SaveCompletionSource(TaskCompletionSource completionSource) + { + var id = Interlocked.Increment(ref lastId); + pendingTasks.TryAdd(id, completionSource); + return id; + } + } +} \ No newline at end of file