Skip to content

Commit 4271b97

Browse files
authored
Use secondary DOMWorld to drive page.Select (#924)
1 parent 0584284 commit 4271b97

File tree

12 files changed

+205
-143
lines changed

12 files changed

+205
-143
lines changed

lib/PuppeteerSharp.Tests/PageTests/SelectTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public async Task ShouldDeselectAllOptionsWhenPassedNoValuesForASelectWithoutMul
112112
"select => Array.from(select.options).every(option => !option.selected)"));
113113
}
114114

115-
[Fact(Skip = "see https://github.com/GoogleChrome/puppeteer/issues/3327")]
115+
[Fact]
116116
public async Task ShouldWorkWhenRedefiningTopLevelEventClass()
117117
{
118118
await Page.GoToAsync(TestConstants.ServerUrl + "/input/select.html");

lib/PuppeteerSharp/ContextPayload.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ internal class ContextPayload
66
{
77
public int Id { get; set; }
88
public ContextPayloadAuxData AuxData { get; set; }
9+
public string Name { get; set; }
910
}
1011
}

lib/PuppeteerSharp/ContextPayloadAuxData.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ internal class ContextPayloadAuxData
66
{
77
public string FrameId { get; set; }
88
public bool IsDefault { get; set; }
9+
public DOMWorldType Type { get; set; }
910
}
1011
}

lib/PuppeteerSharp/DOMWorld.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,17 @@ namespace PuppeteerSharp
1010
internal class DOMWorld
1111
{
1212
private readonly FrameManager _frameManager;
13-
private readonly Frame _frame;
14-
1513
private bool _detached;
16-
1714
private TaskCompletionSource<ExecutionContext> _contextResolveTaskWrapper;
1815
private TaskCompletionSource<ElementHandle> _documentCompletionSource;
1916

2017
internal List<WaitTask> WaitTasks;
18+
internal Frame Frame { get; }
2119

2220
public DOMWorld(FrameManager frameManager, Frame frame)
2321
{
2422
_frameManager = frameManager;
25-
_frame = frame;
23+
Frame = frame;
2624

2725
SetContext(null);
2826

@@ -60,7 +58,7 @@ internal Task<ExecutionContext> GetExecutionContextAsync()
6058
{
6159
if (_detached)
6260
{
63-
throw new PuppeteerException($"Execution Context is not available in detached frame \"{_frame.Url}\"(are you trying to evaluate?)");
61+
throw new PuppeteerException($"Execution Context is not available in detached frame \"{Frame.Url}\"(are you trying to evaluate?)");
6462
}
6563
return _contextResolveTaskWrapper.Task;
6664
}
@@ -142,7 +140,7 @@ await EvaluateFunctionAsync(@"html => {
142140
document.close();
143141
}", html).ConfigureAwait(false);
144142

145-
var watcher = new LifecycleWatcher(_frameManager, _frame, waitUntil, timeout);
143+
var watcher = new LifecycleWatcher(_frameManager, Frame, waitUntil, timeout);
146144
var watcherTask = await Task.WhenAny(
147145
watcher.TimeoutOrTerminationTask,
148146
watcher.LifecycleTask).ConfigureAwait(false);

lib/PuppeteerSharp/DOMWorldType.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Runtime.Serialization;
2+
using Newtonsoft.Json;
3+
using PuppeteerSharp.Helpers.Json;
4+
5+
namespace PuppeteerSharp
6+
{
7+
[JsonConverter(typeof(FlexibleStringEnumConverter), Other)]
8+
internal enum DOMWorldType
9+
{
10+
Other,
11+
[EnumMember(Value = "isolated")]
12+
Isolated
13+
}
14+
}

lib/PuppeteerSharp/ExecutionContext.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@ public class ExecutionContext
2424
private readonly CDPSession _client;
2525
private readonly int _contextId;
2626

27+
internal DOMWorld World { get; }
28+
2729
internal ExecutionContext(
2830
CDPSession client,
2931
ContextPayload contextPayload,
30-
Frame frame)
32+
DOMWorld world)
3133
{
3234
_client = client;
3335
_contextId = contextPayload.Id;
34-
Frame = frame;
36+
World = world;
3537
}
3638

3739
/// <summary>
@@ -40,7 +42,7 @@ internal ExecutionContext(
4042
/// <remarks>
4143
/// NOTE Not every execution context is associated with a frame. For example, workers and extensions have execution contexts that are not associated with frames.
4244
/// </remarks>
43-
public Frame Frame { get; }
45+
public Frame Frame => World?.Frame;
4446

4547
/// <summary>
4648
/// Executes a script in browser context

lib/PuppeteerSharp/Frame.cs

Lines changed: 85 additions & 86 deletions
Large diffs are not rendered by default.

lib/PuppeteerSharp/FrameManager.cs

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ internal class FrameManager
1919
private readonly ConcurrentDictionary<string, Frame> _frames;
2020
private const string RefererHeaderName = "referer";
2121
private readonly AsyncDictionaryHelper<string, Frame> _asyncFrames;
22+
private readonly List<string> _isolatedWorlds = new List<string>();
23+
24+
private const string UtilityWorldName = "__puppeteer_utility_world__";
2225

2326
private FrameManager(CDPSession client, Page page, NetworkManager networkManager)
2427
{
@@ -215,8 +218,12 @@ private void OnExecutionContextsCleared()
215218
while (_contextIdToContext.Count > 0)
216219
{
217220
var contextItem = _contextIdToContext.ElementAt(0);
218-
RemoveContext(contextItem.Value);
219221
_contextIdToContext.Remove(contextItem.Key);
222+
223+
if (contextItem.Value.World != null)
224+
{
225+
contextItem.Value.World.SetContext(null);
226+
}
220227
}
221228
}
222229

@@ -227,26 +234,41 @@ private void OnExecutionContextDestroyed(int executionContextId)
227234
if (context != null)
228235
{
229236
_contextIdToContext.Remove(executionContextId);
230-
RemoveContext(context);
237+
238+
if (context.World != null)
239+
{
240+
context.World.SetContext(null);
241+
}
231242
}
232243
}
233244

234245
private async Task OnExecutionContextCreatedAsync(ContextPayload contextPayload)
235246
{
236-
var frameId = contextPayload.AuxData.IsDefault ? contextPayload.AuxData.FrameId : null;
237-
var frame = !string.IsNullOrEmpty(frameId) ? await GetFrameAsync(frameId) : null;
238-
239-
var context = new ExecutionContext(
240-
Client,
241-
contextPayload,
242-
frame);
243-
244-
_contextIdToContext[contextPayload.Id] = context;
247+
var frameId = contextPayload.AuxData?.FrameId;
248+
var frame = !string.IsNullOrEmpty(frameId) ? await GetFrameAsync(frameId).ConfigureAwait(false) : null;
249+
DOMWorld world = null;
245250

246251
if (frame != null)
247252
{
248-
frame.AddExecutionContext(context);
253+
if (contextPayload.AuxData?.IsDefault == true)
254+
{
255+
world = frame.MainWorld;
256+
}
257+
else if (contextPayload.Name == UtilityWorldName)
258+
{
259+
world = frame.SecondaryWorld;
260+
}
261+
}
262+
if (contextPayload.AuxData?.Type == DOMWorldType.Isolated)
263+
{
264+
_isolatedWorlds.Add(contextPayload.Name);
249265
}
266+
var context = new ExecutionContext(Client, contextPayload, world);
267+
if (world != null)
268+
{
269+
world.SetContext(context);
270+
}
271+
_contextIdToContext[contextPayload.Id] = context;
250272
}
251273

252274
private void OnFrameDetached(BasicFrameResponse e)
@@ -314,14 +336,6 @@ private void OnFrameNavigatedWithinDocument(NavigatedWithinDocumentResponse e)
314336
}
315337
}
316338

317-
private void RemoveContext(ExecutionContext context)
318-
{
319-
if (context.Frame != null)
320-
{
321-
context.Frame.RemoveExecutionContext();
322-
}
323-
}
324-
325339
private void RemoveFramesRecursively(Frame frame)
326340
{
327341
while (frame.ChildFrames.Count > 0)
@@ -365,6 +379,28 @@ private async Task HandleFrameTreeAsync(FrameTree frameTree)
365379
}
366380
}
367381

382+
internal Task EnsureSecondaryDOMWorldAsync() => EnsureSecondaryDOMWorldAsync(UtilityWorldName);
383+
384+
internal async Task EnsureSecondaryDOMWorldAsync(string name)
385+
{
386+
if (_isolatedWorlds.Contains(name))
387+
{
388+
return;
389+
}
390+
_isolatedWorlds.Add(name);
391+
await Client.SendAsync("Page.addScriptToEvaluateOnNewDocument", new PageAddScriptToEvaluateOnNewDocumentRequest
392+
{
393+
Source = $"//# sourceURL={ExecutionContext.EvaluationScriptUrl}",
394+
WorldName = name,
395+
});
396+
await Task.WhenAll(GetFrames().Select(frame => Client.SendAsync("Page.createIsolatedWorld", new PageCreateIsolatedWorldRequest
397+
{
398+
FrameId = frame.Id,
399+
GrantUniveralAccess = true,
400+
WorldName = name
401+
}))).ConfigureAwait(false);
402+
}
403+
368404
internal Task<Frame> GetFrameAsync(string frameId) => _asyncFrames.GetItemAsync(frameId);
369405

370406
#endregion

lib/PuppeteerSharp/Messaging/PageAddScriptToEvaluateOnNewDocumentRequest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
internal class PageAddScriptToEvaluateOnNewDocumentRequest
44
{
55
public string Source { get; set; }
6+
public string WorldName { get; set; }
67
}
78
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace PuppeteerSharp.Messaging
2+
{
3+
internal class PageCreateIsolatedWorldRequest
4+
{
5+
public string FrameId { get; set; }
6+
public string WorldName { get; set; }
7+
public bool GrantUniveralAccess { get; set; }
8+
}
9+
}

0 commit comments

Comments
 (0)