况洋洋
2025-07-04 0d247bd2a17e0f99f3609774a1ce54ae00857997
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// 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 CefSharp.Internals.Tasks;
 
namespace CefSharp.Internals
{
    /// <summary>
    /// MethodRunnerQueue - Async Javascript Binding methods are run
    /// on the ThreadPool sequentially
    /// </summary>
    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 LimitedConcurrencyLevelTaskScheduler taskScheduler = new LimitedConcurrencyLevelTaskScheduler(1);
        private readonly IJavascriptObjectRepositoryInternal repository;
        private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
 
        /// <inheritdoc/>
        public event EventHandler<MethodInvocationCompleteArgs> MethodInvocationComplete;
 
        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="repository">javascript object repository</param>
        public MethodRunnerQueue(IJavascriptObjectRepositoryInternal repository)
        {
            this.repository = repository;
        }
 
        /// <inheritdoc/>
        public void Dispose()
        {
            //Cancel all tasks associated with this MethoRunnerQueue
            cancellationTokenSource.Cancel();
        }
 
        /// <inheritdoc/>
        public void Enqueue(MethodInvocation methodInvocation)
        {
            if(cancellationTokenSource.IsCancellationRequested)
            {
                return;
            }
 
            Task.Factory.StartNew(() =>
            {
                if (cancellationTokenSource.IsCancellationRequested)
                {
                    return;
                }
 
                var result = ExecuteMethodInvocation(methodInvocation);
 
                var handler = MethodInvocationComplete;
                if (!cancellationTokenSource.Token.IsCancellationRequested && handler != null)
                {
                    handler(this, new MethodInvocationCompleteArgs(result));
                }
            }, cancellationTokenSource.Token, TaskCreationOptions.HideScheduler, taskScheduler);
        }
 
        private MethodInvocationResult ExecuteMethodInvocation(MethodInvocation methodInvocation)
        {
            object returnValue = null;
            string exception;
            var success = false;
            var nameConverter = repository.NameConverter;
 
            //make sure we don't throw exceptions in the executor task
            try
            {
                var result = repository.TryCallMethod(methodInvocation.ObjectId, methodInvocation.MethodName, methodInvocation.Parameters.ToArray());
                success = result.Success;
                returnValue = result.ReturnValue;
                exception = result.Exception;
 
                //We don't support Tasks by default
                if (success && returnValue != null && (typeof(Task).IsAssignableFrom(returnValue.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 = returnValue,
                Success = success,
                NameConverter = nameConverter
            };
        }
    }
}