// Copyright © 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. using System; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Schedulers; namespace CefSharp.Internals { public sealed class MethodRunnerQueue : IMethodRunnerQueue { //Limit to 1 task per methodRunnerQueue //https://social.msdn.microsoft.com/Forums/vstudio/en-US/d0bcb415-fb1e-42e4-90f8-c43a088537fb/aborting-a-long-running-task-in-tpl?forum=parallelextensions private readonly TaskFactory methodRunnerQueueTaskFactory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(1)); private readonly JavascriptObjectRepository repository; private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); public event EventHandler MethodInvocationComplete; public MethodRunnerQueue(JavascriptObjectRepository repository) { this.repository = repository; } public void Dispose() { //Cancel all tasks associated with this MethoRunnerQueue cancellationTokenSource.Cancel(); } public void Enqueue(MethodInvocation methodInvocation) { methodRunnerQueueTaskFactory.StartNew(() => { var result = ExecuteMethodInvocation(methodInvocation); var handler = MethodInvocationComplete; if (!cancellationTokenSource.Token.IsCancellationRequested && handler != null) { handler(this, new MethodInvocationCompleteArgs(result)); } }, cancellationTokenSource.Token); } private MethodInvocationResult ExecuteMethodInvocation(MethodInvocation methodInvocation) { object result = null; string exception; var success = false; //make sure we don't throw exceptions in the executor task try { success = repository.TryCallMethod(methodInvocation.ObjectId, methodInvocation.MethodName, methodInvocation.Parameters.ToArray(), out result, out exception); //We don't support Tasks by default if (success && result != null && (typeof(Task).IsAssignableFrom(result.GetType()))) { //Use StringBuilder to improve the formatting/readability of the error message //I'm sure there's a better way I just cannot remember of the top of my head so going //with this for now, as it's only for error scenaiors I'm not concerned about performance. var builder = new System.Text.StringBuilder(); builder.AppendLine("Your method returned a Task which is not supported by default you must set CefSharpSettings.ConcurrentTaskExecution = true; before creating your first ChromiumWebBrowser instance."); builder.AppendLine("This will likely change to the default at some point in the near future, subscribe to the issue link below to be notified of any changes."); builder.AppendLine("See https://github.com/cefsharp/CefSharp/issues/2758 for more details, please report any issues you have there, make sure you have an example ready that reproduces your problem."); success = false; result = null; exception = builder.ToString(); } } catch (Exception e) { exception = e.Message; } return new MethodInvocationResult { BrowserId = methodInvocation.BrowserId, CallbackId = methodInvocation.CallbackId, FrameId = methodInvocation.FrameId, Message = exception, Result = result, Success = success }; } } }