Skip to content

Commit 74c7516

Browse files
committed
Bidi workers
1 parent a284969 commit 74c7516

File tree

7 files changed

+195
-26
lines changed

7 files changed

+195
-26
lines changed

lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,21 +1312,6 @@
13121312
"FAIL"
13131313
]
13141314
},
1315-
{
1316-
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
1317-
"testIdPattern": "[worker.spec] *",
1318-
"platforms": [
1319-
"darwin",
1320-
"linux",
1321-
"win32"
1322-
],
1323-
"parameters": [
1324-
"webDriverBiDi"
1325-
],
1326-
"expectations": [
1327-
"FAIL"
1328-
]
1329-
},
13301315
{
13311316
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
13321317
"testIdPattern": "[ChromeLauncher.test.ts] *",

lib/PuppeteerSharp/Bidi/BidiFrame.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,20 @@ private void Initialize()
604604
BidiPage.OnPageError(new PageErrorEventArgs(fullStack));
605605
}
606606
};
607+
608+
// Wire up worker events
609+
if (_realms.Default is BidiFrameRealm defaultFrameRealm)
610+
{
611+
defaultFrameRealm.WindowRealm.Worker += (sender, args) =>
612+
{
613+
var worker = BidiWebWorker.From(this, args.Realm);
614+
args.Realm.Destroyed += (o, eventArgs) =>
615+
{
616+
BidiPage.OnWorkerDestroyed(worker);
617+
};
618+
BidiPage.OnWorkerCreated(worker);
619+
};
620+
}
607621
}
608622

609623
private void CreateFrameTarget(BrowsingContext browsingContext)

lib/PuppeteerSharp/Bidi/BidiFrameRealm.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ internal class BidiFrameRealm(WindowRealm realm, BidiFrame frame) : BidiRealm(re
3535

3636
internal BidiFrame Frame => frame;
3737

38+
internal WindowRealm WindowRealm => _realm;
39+
3840
public static BidiFrameRealm From(WindowRealm realm, BidiFrame frame)
3941
{
4042
var frameRealm = new BidiFrameRealm(realm, frame);

lib/PuppeteerSharp/Bidi/BidiWebWorker.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,42 @@
2020
// * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
// * SOFTWARE.
2222

23+
using System;
2324
using System.Threading.Tasks;
25+
using PuppeteerSharp.Bidi.Core;
2426

2527
namespace PuppeteerSharp.Bidi;
2628

2729
internal class BidiWebWorker : WebWorker
2830
{
29-
public BidiWebWorker(string url) : base(url)
31+
private readonly BidiFrame _frame;
32+
private readonly BidiWorkerRealm _realm;
33+
34+
private BidiWebWorker(BidiFrame frame, DedicatedWorkerRealm realm) : base(realm.Origin)
35+
{
36+
_frame = frame;
37+
_realm = BidiWorkerRealm.From(realm, this);
38+
}
39+
40+
public override ICDPSession Client => throw new NotSupportedException();
41+
42+
public override Task CloseAsync()
3043
{
44+
// BiDi doesn't support closing workers directly
45+
// Workers are closed when they terminate themselves or when the owning page/frame closes
46+
throw new NotSupportedException("WebWorker.CloseAsync() is not supported in BiDi protocol");
3147
}
3248

33-
public override ICDPSession Client { get; }
49+
internal static BidiWebWorker From(BidiFrame frame, DedicatedWorkerRealm realm)
50+
{
51+
return new BidiWebWorker(frame, realm);
52+
}
53+
54+
internal override IsolatedWorld World => throw new NotSupportedException();
55+
56+
internal override Realm GetMainRealm() => _realm;
3457

35-
internal override IsolatedWorld World { get; }
58+
internal BidiFrame Frame => _frame;
3659

37-
public override Task CloseAsync() => throw new System.NotImplementedException();
60+
internal TimeoutSettings TimeoutSettings => _frame.TimeoutSettings;
3861
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// * MIT License
2+
// *
3+
// * Copyright (c) Darío Kondratiuk
4+
// *
5+
// * Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// * of this software and associated documentation files (the "Software"), to deal
7+
// * in the Software without restriction, including without limitation the rights
8+
// * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// * copies of the Software, and to permit persons to whom the Software is
10+
// * furnished to do so, subject to the following conditions:
11+
// *
12+
// * The above copyright notice and this permission notice shall be included in all
13+
// * copies or substantial portions of the Software.
14+
// *
15+
// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// * SOFTWARE.
22+
23+
using System.Threading.Tasks;
24+
using PuppeteerSharp.Bidi.Core;
25+
using PuppeteerSharp.Helpers;
26+
27+
namespace PuppeteerSharp.Bidi;
28+
29+
internal class BidiWorkerRealm : BidiRealm
30+
{
31+
private readonly DedicatedWorkerRealm _realm;
32+
private readonly BidiWebWorker _worker;
33+
private readonly TaskQueue _puppeteerUtilQueue = new();
34+
private IJSHandle _puppeteerUtil;
35+
36+
private BidiWorkerRealm(DedicatedWorkerRealm realm, BidiWebWorker worker)
37+
: base(realm, worker.TimeoutSettings)
38+
{
39+
_realm = realm;
40+
_worker = worker;
41+
}
42+
43+
internal override IEnvironment Environment => _worker;
44+
45+
internal BidiWebWorker Worker => _worker;
46+
47+
public static BidiWorkerRealm From(DedicatedWorkerRealm realm, BidiWebWorker worker)
48+
{
49+
var workerRealm = new BidiWorkerRealm(realm, worker);
50+
workerRealm.Initialize();
51+
return workerRealm;
52+
}
53+
54+
public override async Task<IJSHandle> GetPuppeteerUtilAsync()
55+
{
56+
var scriptInjector = _realm.Session.ScriptInjector;
57+
58+
await _puppeteerUtilQueue.Enqueue(async () =>
59+
{
60+
if (_puppeteerUtil == null)
61+
{
62+
await scriptInjector.InjectAsync(
63+
async (script) =>
64+
{
65+
if (_puppeteerUtil != null)
66+
{
67+
await _puppeteerUtil.DisposeAsync().ConfigureAwait(false);
68+
}
69+
70+
_puppeteerUtil = await EvaluateExpressionHandleAsync(script).ConfigureAwait(false);
71+
},
72+
_puppeteerUtil == null).ConfigureAwait(false);
73+
}
74+
}).ConfigureAwait(false);
75+
76+
return _puppeteerUtil;
77+
}
78+
79+
protected override void Initialize()
80+
{
81+
base.Initialize();
82+
83+
_realm.Destroyed += (sender, args) => Dispose();
84+
_realm.Updated += (sender, args) =>
85+
{
86+
// Reset PuppeteerUtil when the realm is updated
87+
_puppeteerUtil = null;
88+
TaskManager.RerunAll();
89+
};
90+
}
91+
}

lib/PuppeteerSharp/Bidi/Core/DedicatedWorkerRealm.cs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,27 @@
2020
// * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
// * SOFTWARE.
2222

23+
using System;
24+
using System.Collections.Concurrent;
2325
using System.Linq;
2426
using PuppeteerSharp.Helpers;
27+
using WebDriverBiDi.Script;
2528

2629
namespace PuppeteerSharp.Bidi.Core;
2730

28-
internal class DedicatedWorkerRealm : Realm
31+
internal class DedicatedWorkerRealm : Realm, IDedicatedWorkerOwnerRealm
2932
{
3033
private readonly ConcurrentSet<IDedicatedWorkerOwnerRealm> _owners = [];
34+
private readonly ConcurrentDictionary<string, DedicatedWorkerRealm> _workers = [];
3135

3236
private DedicatedWorkerRealm(BrowsingContext context, IDedicatedWorkerOwnerRealm owner, string id, string origin)
3337
: base(context, id, origin)
3438
{
3539
_owners.Add(owner);
3640
}
3741

42+
public event EventHandler<WorkerRealmEventArgs> Worker;
43+
3844
public override Session Session => _owners.FirstOrDefault()?.Session;
3945

4046
public static DedicatedWorkerRealm From(BrowsingContext context, IDedicatedWorkerOwnerRealm owner, string id, string origin)
@@ -44,8 +50,54 @@ public static DedicatedWorkerRealm From(BrowsingContext context, IDedicatedWorke
4450
return realm;
4551
}
4652

53+
public override void Dispose()
54+
{
55+
foreach (var worker in _workers.Values)
56+
{
57+
worker.Dispose();
58+
}
59+
60+
base.Dispose();
61+
}
62+
4763
private void Initialize()
4864
{
49-
throw new System.NotImplementedException();
65+
// Listen to realm destruction
66+
Session.Driver.Script.OnRealmDestroyed.AddObserver(OnRealmDestroyed);
67+
68+
// Listen to nested worker creation
69+
Session.Driver.Script.OnRealmCreated.AddObserver(OnDedicatedRealmCreated);
5070
}
71+
72+
private void OnRealmDestroyed(RealmDestroyedEventArgs args)
73+
{
74+
if (args.RealmId != Id)
75+
{
76+
return;
77+
}
78+
79+
Dispose("Realm already destroyed.");
80+
}
81+
82+
private void OnDedicatedRealmCreated(RealmCreatedEventArgs args)
83+
{
84+
if (args.Type != RealmType.DedicatedWorker)
85+
{
86+
return;
87+
}
88+
89+
var dedicatedWorkerInfo = args.As<DedicatedWorkerRealmInfo>();
90+
91+
if (!dedicatedWorkerInfo.Owners.Contains(Id))
92+
{
93+
return;
94+
}
95+
96+
var realm = DedicatedWorkerRealm.From(Context, this, dedicatedWorkerInfo.RealmId, dedicatedWorkerInfo.Origin);
97+
_workers.TryAdd(realm.Id, realm);
98+
realm.Destroyed += (sender, args) => _workers.TryRemove(realm.Id, out _);
99+
OnWorker(realm);
100+
}
101+
102+
private void OnWorker(DedicatedWorkerRealm realm) => Worker?.Invoke(this, new WorkerRealmEventArgs(realm));
51103
}

lib/PuppeteerSharp/WebWorker.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,6 @@ internal WebWorker(string url)
3737
/// </summary>
3838
public abstract ICDPSession Client { get; }
3939

40-
/// <inheritdoc/>
41-
Realm IEnvironment.MainRealm => World;
42-
43-
internal abstract IsolatedWorld World { get; }
44-
4540
/// <summary>
4641
/// Executes a script in browser context.
4742
/// </summary>
@@ -99,5 +94,12 @@ public async Task<IJSHandle> EvaluateExpressionHandleAsync(string script)
9994
/// </summary>
10095
/// <returns>A <see cref="Task"/> that completes when the worker is closed.</returns>
10196
public abstract Task CloseAsync();
97+
98+
internal abstract IsolatedWorld World { get; }
99+
100+
internal virtual Realm GetMainRealm() => World;
101+
102+
/// <inheritdoc/>
103+
Realm IEnvironment.MainRealm => GetMainRealm();
102104
}
103105
}

0 commit comments

Comments
 (0)