Skip to content

Commit 7cde7c4

Browse files
authored
Atomically get Puppeteer utilities (#2236)
* Atomically get Puppeteer utilities * Inject util in context * cr
1 parent a5ae465 commit 7cde7c4

File tree

6 files changed

+45
-55
lines changed

6 files changed

+45
-55
lines changed

lib/PuppeteerSharp.Tests/InjectedTests/InjectedTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public async Task ShouldWork()
2424
PuppeteerUtil => {
2525
return typeof PuppeteerUtil === 'object';
2626
}",
27-
await world.GetPuppeteerUtilAsync());
27+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)));
2828
Assert.True(result);
2929
}
3030

@@ -37,7 +37,7 @@ public async Task CreateFunctionShouldWork()
3737
.PuppeteerWorld.EvaluateFunctionAsync<int>(@"({createFunction}, fnString) => {
3838
return createFunction(fnString)(4);
3939
}",
40-
await world.GetPuppeteerUtilAsync(),
40+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)),
4141
"() => 4");
4242
Assert.Equal(4, result);
4343
}

lib/PuppeteerSharp/CustomQueriesManager.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace PuppeteerSharp
1010
{
1111
internal class CustomQueriesManager
1212
{
13-
private static readonly string[] CustomQuerySeparators = new[] { "=", "/" };
13+
private static readonly string[] _customQuerySeparators = new[] { "=", "/" };
1414
private readonly Dictionary<string, PuppeteerQueryHandler> _internalQueryHandlers;
1515
private readonly Dictionary<string, PuppeteerQueryHandler> _queryHandlers = new();
1616
private readonly PuppeteerQueryHandler _pierceHandler = CreatePuppeteerQueryHandler(new CustomQueryHandler
@@ -194,7 +194,7 @@ internal void RegisterCustomQueryHandler(string name, CustomQueryHandler queryHa
194194

195195
foreach (var kv in handlers)
196196
{
197-
foreach (var separator in CustomQuerySeparators)
197+
foreach (var separator in _customQuerySeparators)
198198
{
199199
var prefix = $"{kv.Key}{separator}";
200200

@@ -231,11 +231,10 @@ private static PuppeteerQueryHandler CreatePuppeteerQueryHandler(CustomQueryHand
231231
{
232232
internalHandler.QueryOne = async (IElementHandle element, string selector) =>
233233
{
234-
var handle = element as JSHandle;
235234
var jsHandle = await element.EvaluateFunctionHandleAsync(
236235
handler.QueryOne,
237236
selector,
238-
await handle.ExecutionContext.World.GetPuppeteerUtilAsync().ConfigureAwait(false))
237+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)))
239238
.ConfigureAwait(false);
240239
if (jsHandle is ElementHandle elementHandle)
241240
{
@@ -280,11 +279,10 @@ await handle.ExecutionContext.World.GetPuppeteerUtilAsync().ConfigureAwait(false
280279
{
281280
internalHandler.QueryAll = async (IElementHandle element, string selector) =>
282281
{
283-
var handle = element as JSHandle;
284282
var jsHandle = await element.EvaluateFunctionHandleAsync(
285283
handler.QueryAll,
286284
selector,
287-
await handle.ExecutionContext.World.GetPuppeteerUtilAsync().ConfigureAwait(false))
285+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)))
288286
.ConfigureAwait(false);
289287
var properties = await jsHandle.GetPropertiesAsync().ConfigureAwait(false);
290288
var result = new List<ElementHandle>();

lib/PuppeteerSharp/ExecutionContext.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using System.Linq;
45
using System.Numerics;
6+
using System.Reflection;
57
using System.Text.RegularExpressions;
68
using System.Threading.Tasks;
79
using Newtonsoft.Json.Linq;
@@ -16,8 +18,10 @@ public class ExecutionContext : IExecutionContext
1618
internal const string EvaluationScriptUrl = "__puppeteer_evaluation_script__";
1719

1820
private static readonly Regex _sourceUrlRegex = new(@"^[\040\t]*\/\/[@#] sourceURL=\s*\S*?\s*$", RegexOptions.Multiline);
21+
private static string _injectedSource;
1922

2023
private readonly string _evaluationScriptSuffix = $"//# sourceURL={EvaluationScriptUrl}";
24+
private IJSHandle _puppeteerUtil;
2125

2226
internal ExecutionContext(
2327
CDPSession client,
@@ -89,6 +93,17 @@ public async Task<IJSHandle> QueryObjectsAsync(IJSHandle prototypeHandle)
8993
return CreateJSHandle(response.Objects);
9094
}
9195

96+
internal async Task<IJSHandle> GetPuppeteerUtilAsync()
97+
{
98+
if (_puppeteerUtil == null)
99+
{
100+
var injectedSource = GetInjectedSource();
101+
_puppeteerUtil = await EvaluateExpressionHandleAsync(injectedSource).ConfigureAwait(false);
102+
}
103+
104+
return _puppeteerUtil;
105+
}
106+
92107
internal IJSHandle CreateJSHandle(RemoteObject remoteObject)
93108
=> remoteObject.Subtype == RemoteObjectSubtype.Node && Frame != null
94109
? new ElementHandle(this, Client, remoteObject, Frame, ((Frame)Frame).FrameManager.Page, ((Frame)Frame).FrameManager)
@@ -120,6 +135,22 @@ internal async Task<IElementHandle> AdoptElementHandleAsync(IElementHandle eleme
120135
return CreateJSHandle(obj.Object) as ElementHandle;
121136
}
122137

138+
private static string GetInjectedSource()
139+
{
140+
if (string.IsNullOrEmpty(_injectedSource))
141+
{
142+
var assembly = Assembly.GetExecutingAssembly();
143+
var resourceName = "PuppeteerSharp.Injected.injected.js";
144+
145+
using var stream = assembly.GetManifestResourceStream(resourceName);
146+
using var reader = new StreamReader(stream);
147+
var fileContent = reader.ReadToEnd();
148+
_injectedSource = fileContent;
149+
}
150+
151+
return _injectedSource;
152+
}
153+
123154
private static string GetExceptionMessage(EvaluateExceptionResponseDetails exceptionDetails)
124155
{
125156
if (exceptionDetails.Exception != null)

lib/PuppeteerSharp/Frame.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ public async Task<IElementHandle> AddStyleTagAsync(AddTagOptions options)
218218
await promise;
219219
return element;
220220
}",
221-
await PuppeteerWorld.GetPuppeteerUtilAsync().ConfigureAwait(false),
221+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)),
222222
options.Url,
223223
options.Id,
224224
options.Type,
@@ -283,7 +283,7 @@ public async Task<IElementHandle> AddScriptTagAsync(AddTagOptions options)
283283
await promise;
284284
return script;
285285
}",
286-
await PuppeteerWorld.GetPuppeteerUtilAsync().ConfigureAwait(false),
286+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)),
287287
options.Url,
288288
options.Id,
289289
options.Type,

lib/PuppeteerSharp/IsolatedWorld.cs

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using System;
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
4-
using System.IO;
5-
using System.Reflection;
64
using System.Threading.Tasks;
75
using Microsoft.Extensions.Logging;
86
using Newtonsoft.Json.Linq;
@@ -21,7 +19,6 @@ namespace PuppeteerSharp
2119

2220
internal class IsolatedWorld
2321
{
24-
private static string _injectedSource;
2522
private readonly FrameManager _frameManager;
2623
private readonly TimeoutSettings _timeoutSettings;
2724
private readonly CDPSession _client;
@@ -56,10 +53,6 @@ public IsolatedWorld(
5653

5754
internal ConcurrentDictionary<string, Delegate> BoundFunctions { get; } = new();
5855

59-
internal TaskCompletionSource<IJSHandle> PuppeteerUtilTaskCompletionSource { get; private set; } = new(TaskCreationOptions.RunContinuationsAsynchronously);
60-
61-
internal Task<IJSHandle> GetPuppeteerUtilAsync() => PuppeteerUtilTaskCompletionSource.Task;
62-
6356
internal async Task AddBindingToContextAsync(ExecutionContext context, string name)
6457
{
6558
// Previous operation added the binding so we are done.
@@ -282,7 +275,7 @@ internal async Task<IElementHandle> WaitForSelectorInPageAsync(string queryOne,
282275

283276
var args = new List<object>
284277
{
285-
await GetPuppeteerUtilAsync().ConfigureAwait(false),
278+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)),
286279
queryOne,
287280
selector,
288281
root,
@@ -438,7 +431,6 @@ internal void ClearContext()
438431
{
439432
_documentTask = null;
440433
_contextResolveTaskWrapper = new TaskCompletionSource<ExecutionContext>(TaskCreationOptions.RunContinuationsAsynchronously);
441-
PuppeteerUtilTaskCompletionSource = new TaskCompletionSource<IJSHandle>(TaskCreationOptions.RunContinuationsAsynchronously);
442434
}
443435

444436
internal void SetContext(ExecutionContext context)
@@ -448,42 +440,11 @@ internal void SetContext(ExecutionContext context)
448440
throw new ArgumentNullException(nameof(context));
449441
}
450442

451-
_ = InjectPuppeteerUtil(context);
452443
_ctxBindings.Clear();
453444
_contextResolveTaskWrapper.TrySetResult(context);
454445
TaskManager.RerunAll();
455446
}
456447

457-
private static string GetInjectedSource()
458-
{
459-
if (string.IsNullOrEmpty(_injectedSource))
460-
{
461-
var assembly = Assembly.GetExecutingAssembly();
462-
var resourceName = "PuppeteerSharp.Injected.injected.js";
463-
464-
using var stream = assembly.GetManifestResourceStream(resourceName);
465-
using var reader = new StreamReader(stream);
466-
var fileContent = reader.ReadToEnd();
467-
_injectedSource = fileContent;
468-
}
469-
470-
return _injectedSource;
471-
}
472-
473-
private async Task InjectPuppeteerUtil(ExecutionContext context)
474-
{
475-
try
476-
{
477-
var injectedSource = GetInjectedSource();
478-
var handle = await context.EvaluateExpressionHandleAsync(injectedSource).ConfigureAwait(false);
479-
PuppeteerUtilTaskCompletionSource.TrySetResult(handle);
480-
}
481-
catch (Exception ex)
482-
{
483-
_logger.LogError(ex.ToString());
484-
}
485-
}
486-
487448
private async void Client_MessageReceived(object sender, MessageEventArgs e)
488449
{
489450
try

lib/PuppeteerSharp/WaitTask.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ internal async Task Rerun()
105105
}",
106106
new object[]
107107
{
108-
await _isolatedWorld.GetPuppeteerUtilAsync().ConfigureAwait(false),
109-
_pollingInterval,
110-
_fn,
108+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)),
109+
_pollingInterval,
110+
_fn,
111111
}.Concat(_args).ToArray()).ConfigureAwait(false);
112112
}
113113
else if (_polling == WaitForFunctionPollingOption.Raf)
@@ -122,7 +122,7 @@ await _isolatedWorld.GetPuppeteerUtilAsync().ConfigureAwait(false),
122122
}",
123123
new object[]
124124
{
125-
await _isolatedWorld.GetPuppeteerUtilAsync().ConfigureAwait(false),
125+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)),
126126
_fn,
127127
}.Concat(_args).ToArray()).ConfigureAwait(false);
128128
}
@@ -138,7 +138,7 @@ await _isolatedWorld.GetPuppeteerUtilAsync().ConfigureAwait(false),
138138
}",
139139
new object[]
140140
{
141-
await _isolatedWorld.GetPuppeteerUtilAsync().ConfigureAwait(false),
141+
new LazyArg(async context => await context.GetPuppeteerUtilAsync().ConfigureAwait(false)),
142142
_root,
143143
_fn,
144144
}.Concat(_args).ToArray()).ConfigureAwait(false);

0 commit comments

Comments
 (0)