Skip to content

Commit eca1640

Browse files
kblokjnyrup
andauthoredJul 3, 2024··
Ensure existing targets are attached to pages (#2670)
* Ensure existing targets are attached to pages * cr * Update lib/PuppeteerSharp/Cdp/CdpPage.cs Co-authored-by: Jonas Nyrup <[email protected]> * Don't fail close on dispose * Don't close pages from a connection on Dispose * change some browser disconnection stuff * Update lib/PuppeteerSharp/Browser.cs * Update lib/PuppeteerSharp/Cdp/CdpBrowser.cs * Update lib/PuppeteerSharp/Cdp/CdpBrowser.cs * Update lib/PuppeteerSharp/Cdp/CdpBrowser.cs * Add empty line --------- Co-authored-by: Jonas Nyrup <[email protected]>
1 parent 5e9f4a5 commit eca1640

File tree

12 files changed

+134
-12
lines changed

12 files changed

+134
-12
lines changed
 

‎lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.upstream.json

+14
Original file line numberDiff line numberDiff line change
@@ -2943,6 +2943,20 @@
29432943
"expectations": ["FAIL"],
29442944
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
29452945
},
2946+
{
2947+
"testIdPattern": "[oopif.spec] OOPIF should recover cross-origin frames on reconnect",
2948+
"platforms": ["darwin", "linux", "win32"],
2949+
"parameters": ["chrome", "webDriverBiDi"],
2950+
"expectations": ["FAIL"],
2951+
"comment": "See https://github.com/GoogleChromeLabs/chromium-bidi/issues/2362"
2952+
},
2953+
{
2954+
"testIdPattern": "[oopif.spec] OOPIF should recover cross-origin frames on reconnect",
2955+
"platforms": ["darwin", "linux", "win32"],
2956+
"parameters": ["firefox", "webDriverBiDi"],
2957+
"expectations": ["FAIL"],
2958+
"comment": "Firefox does not support multiple sessions in BiDi."
2959+
},
29462960
{
29472961
"testIdPattern": "[oopif.spec] OOPIF should support lazy OOP frames",
29482962
"platforms": ["darwin", "linux", "win32"],

‎lib/PuppeteerSharp.Tests/OOPIFTests/OOPIFTests.cs

+28
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,34 @@ await FrameUtils.AttachFrameAsync(
104104
StringAssert.Contains("frame.html", await frame2.EvaluateExpressionAsync<string>("document.location.href"));
105105
}
106106

107+
[Test, Retry(2), PuppeteerTest("oopif.spec", "OOPIF", "should recover cross-origin frames on reconnect")]
108+
public async Task ShouldRecoverCrossOriginFramesOnReconnect()
109+
{
110+
await Page.GoToAsync(TestConstants.EmptyPage);
111+
var frame1Task = Page.WaitForFrameAsync((frame) => frame != Page.MainFrame && frame.ParentFrame == Page.MainFrame);
112+
var frame2Task = Page.WaitForFrameAsync((frame) => frame != Page.MainFrame && frame.ParentFrame != Page.MainFrame);
113+
114+
await FrameUtils.AttachFrameAsync(
115+
Page,
116+
"frame1",
117+
TestConstants.CrossProcessHttpPrefix + "/frames/one-frame.html"
118+
);
119+
120+
await Task.WhenAll(frame1Task, frame2Task);
121+
var dump1 = await FrameUtils.DumpFramesAsync(Page.MainFrame);
122+
123+
await using var browserTwo = await Puppeteer.ConnectAsync(new ConnectOptions()
124+
{
125+
BrowserWSEndpoint = Browser.WebSocketEndpoint,
126+
});
127+
128+
var pages = await browserTwo.PagesAsync();
129+
var emptyPages = pages.Where(page => page.Url == TestConstants.EmptyPage).ToArray();
130+
Assert.AreEqual(1, emptyPages.Length);
131+
var dump2 = await FrameUtils.DumpFramesAsync(emptyPages[0].MainFrame);
132+
Assert.AreEqual(dump1, dump2);
133+
}
134+
107135
[Test, Retry(2), PuppeteerTest("oopif.spec", "OOPIF", "should support OOP iframes getting detached")]
108136
public async Task ShouldSupportOopIframesGettingDetached()
109137
{

‎lib/PuppeteerSharp/Browser.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,16 @@ public void Dispose()
174174
/// <returns>ValueTask.</returns>
175175
public async ValueTask DisposeAsync()
176176
{
177-
await CloseAsync().ConfigureAwait(false);
177+
// On disposal, the browser doesn't get closed. It gets disconnected.
178+
if (Launcher == null)
179+
{
180+
Disconnect();
181+
}
182+
else
183+
{
184+
await CloseAsync().ConfigureAwait(false);
185+
}
186+
178187
await ScreenshotTaskQueue.DisposeAsync().ConfigureAwait(false);
179188
GC.SuppressFinalize(this);
180189
}

‎lib/PuppeteerSharp/Cdp/CdpBrowser.cs

+3-4
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ public class CdpBrowser : Browser
4040

4141
private readonly ConcurrentDictionary<string, CdpBrowserContext> _contexts;
4242
private readonly ILogger<Browser> _logger;
43-
private readonly Func<Target, bool> _targetFilterCallback;
4443
private Task _closeTask;
4544

4645
internal CdpBrowser(
@@ -58,7 +57,7 @@ internal CdpBrowser(
5857
DefaultViewport = defaultViewport;
5958
Launcher = launcher;
6059
Connection = connection;
61-
_targetFilterCallback = targetFilter ?? (_ => true);
60+
var targetFilterCallback = targetFilter ?? (_ => true);
6261
_logger = Connection.LoggerFactory.CreateLogger<Browser>();
6362
IsPageTargetFunc =
6463
isPageTargetFunc ??
@@ -74,14 +73,14 @@ internal CdpBrowser(
7473
TargetManager = new FirefoxTargetManager(
7574
connection,
7675
CreateTarget,
77-
_targetFilterCallback);
76+
targetFilterCallback);
7877
}
7978
else
8079
{
8180
TargetManager = new ChromeTargetManager(
8281
connection,
8382
CreateTarget,
84-
_targetFilterCallback,
83+
targetFilterCallback,
8584
this,
8685
launcher?.Options?.Timeout ?? Puppeteer.DefaultTimeout);
8786
}

‎lib/PuppeteerSharp/Cdp/CdpPage.cs

+20
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ private CdpPage(
113113
TaskScheduler.Default);
114114

115115
SetupPrimaryTargetListeners();
116+
AttachExistingTargets();
116117
}
117118

118119
/// <inheritdoc cref="CDPSession"/>
@@ -1309,4 +1310,23 @@ private Task ResetBackgroundColorAndViewportAsync(ScreenshotOptions options)
13091310
: Task.CompletedTask;
13101311
return Task.WhenAll(omitBackgroundTask, setViewPortTask);
13111312
}
1313+
1314+
private void AttachExistingTargets()
1315+
{
1316+
List<ITarget> queue = [];
1317+
queue.AddRange(_targetManager.GetChildTargets(PrimaryTarget));
1318+
1319+
for (var idx = 0; idx < queue.Count; idx++)
1320+
{
1321+
var next = (CdpTarget)queue[idx];
1322+
var session = next.Session;
1323+
1324+
if (session != null)
1325+
{
1326+
OnAttachedToTarget(this, new SessionEventArgs(session));
1327+
}
1328+
1329+
queue.AddRange(_targetManager.GetChildTargets(next));
1330+
}
1331+
}
13121332
}

‎lib/PuppeteerSharp/Cdp/ChromeTargetManager.cs

+10-2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ await _connection.SendAsync(
8989
await _initializeCompletionSource.Task.ConfigureAwait(false);
9090
}
9191

92+
public IEnumerable<ITarget> GetChildTargets(ITarget target) => target.ChildTargets;
93+
9294
private void StoreExistingTargetsForInit()
9395
{
9496
foreach (var kv in _discoveredTargetsByTargetId)
@@ -101,8 +103,11 @@ private void StoreExistingTargetsForInit()
101103
null,
102104
_browser.ScreenshotTaskQueue);
103105

104-
if ((_targetFilterFunc == null || _targetFilterFunc(targetForFilter)) &&
105-
kv.Value.Type != TargetType.Browser)
106+
// Targets from extensions and the browser that will not be
107+
// auto-attached. Therefore, we should not add them to
108+
// #targetsIdsForInit.
109+
var skipTarget = kv.Value.Type == TargetType.Browser || kv.Value.Url.StartsWith("chrome-extension://", StringComparison.OrdinalIgnoreCase);
110+
if (!skipTarget && (_targetFilterFunc == null || _targetFilterFunc(targetForFilter)))
106111
{
107112
_targetsIdsForInit.Add(kv.Key);
108113
}
@@ -292,6 +297,8 @@ private async Task OnAttachedToTargetAsync(object sender, TargetAttachedToTarget
292297
_attachedTargetsBySessionId.TryAdd(session.Id, target);
293298
}
294299

300+
var parentTarget = parentSession?.Target;
301+
parentTarget?.AddChildTarget(target);
295302
(parentSession ?? parentConnection as CDPSession)?.OnSessionReady(session);
296303

297304
await EnsureTargetsIdsForInitAsync().ConfigureAwait(false);
@@ -380,6 +387,7 @@ private void OnDetachedFromTarget(object sender, TargetDetachedFromTargetRespons
380387
return;
381388
}
382389

390+
(sender as CdpCDPSession)?.Target.RemoveChildTarget(target);
383391
_attachedTargetsByTargetId.TryRemove(target.TargetId, out _);
384392
TargetGone?.Invoke(this, new TargetChangedArgs { Target = target });
385393
}

‎lib/PuppeteerSharp/Cdp/FirefoxTargetManager.cs

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ public async Task InitializeAsync()
6060

6161
public AsyncDictionaryHelper<string, CdpTarget> GetAvailableTargets() => _availableTargetsByTargetId;
6262

63+
public IEnumerable<ITarget> GetChildTargets(ITarget target) => [];
64+
6365
private void OnMessageReceived(object sender, MessageEventArgs e)
6466
{
6567
try

‎lib/PuppeteerSharp/Cdp/ITargetManager.cs

+9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
24
using System.Threading.Tasks;
35
using PuppeteerSharp.Cdp;
46
using PuppeteerSharp.Helpers;
@@ -41,5 +43,12 @@ internal interface ITargetManager
4143
/// </summary>
4244
/// <returns>A task that resolves when all the tasks are completed.</returns>
4345
Task InitializeAsync();
46+
47+
/// <summary>
48+
/// Returns the target's child.
49+
/// </summary>
50+
/// <param name="target">Target to evaluate.</param>
51+
/// <returns>A list of targets.</returns>
52+
IEnumerable<ITarget> GetChildTargets(ITarget target);
4453
}
4554
}

‎lib/PuppeteerSharp/ITarget.cs

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Generic;
12
using System.Threading.Tasks;
23

34
namespace PuppeteerSharp
@@ -44,6 +45,11 @@ public interface ITarget
4445
/// <value>The URL.</value>
4546
string Url { get; }
4647

48+
/// <summary>
49+
/// Gets the target's child targets.
50+
/// </summary>
51+
IEnumerable<ITarget> ChildTargets { get; }
52+
4753
/// <summary>
4854
/// Creates a Chrome Devtools Protocol session attached to the target.
4955
/// </summary>

‎lib/PuppeteerSharp/Page.cs

+16-1
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,22 @@ public void Dispose()
750750
/// <inheritdoc/>
751751
public async ValueTask DisposeAsync()
752752
{
753-
await CloseAsync().ConfigureAwait(false);
753+
try
754+
{
755+
// We don't want to close the page if we're connected to the browser using `Connect`.
756+
if (Browser.Launcher == null)
Has conversations. Original line has conversations.
757+
{
758+
return;
759+
}
760+
761+
await CloseAsync().ConfigureAwait(false);
762+
}
763+
catch
764+
{
765+
// Closing on dispose might not be bulletproof.
766+
// If the user didn't close the page explicitly, we won't fail.
767+
}
768+
754769
GC.SuppressFinalize(this);
755770
}
756771

‎lib/PuppeteerSharp/PuppeteerSharp.csproj

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
<Description>Headless Browser .NET API</Description>
1313
<PackageId>PuppeteerSharp</PackageId>
1414
<PackageReleaseNotes></PackageReleaseNotes>
15-
<PackageVersion>18.0.2</PackageVersion>
16-
<ReleaseVersion>18.0.2</ReleaseVersion>
17-
<AssemblyVersion>18.0.2</AssemblyVersion>
18-
<FileVersion>18.0.2</FileVersion>
15+
<PackageVersion>18.0.3</PackageVersion>
16+
<ReleaseVersion>18.0.3</ReleaseVersion>
17+
<AssemblyVersion>18.0.3</AssemblyVersion>
18+
<FileVersion>18.0.3</FileVersion>
1919
<SynchReleaseVersion>false</SynchReleaseVersion>
2020
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
2121
<DebugType>embedded</DebugType>

‎lib/PuppeteerSharp/Target.cs

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics;
34
using System.Threading.Tasks;
5+
using PuppeteerSharp.Cdp;
6+
using PuppeteerSharp.Helpers;
47

58
namespace PuppeteerSharp
69
{
@@ -10,6 +13,8 @@ namespace PuppeteerSharp
1013
[DebuggerDisplay("Target {Type} - {Url}")]
1114
public abstract class Target : ITarget
1215
{
16+
private readonly ConcurrentSet<ITarget> _childTargets = [];
17+
1318
internal Target(
1419
TargetInfo targetInfo,
1520
CDPSession session,
@@ -47,6 +52,9 @@ internal Target(
4752
/// <inheritdoc/>
4853
IBrowserContext ITarget.BrowserContext => BrowserContext;
4954

55+
/// <inheritdoc/>
56+
IEnumerable<ITarget> ITarget.ChildTargets => _childTargets;
57+
5058
internal BrowserContext BrowserContext { get; }
5159

5260
internal Browser Browser => BrowserContext.Browser;
@@ -80,5 +88,9 @@ internal Target(
8088

8189
/// <inheritdoc/>
8290
public abstract Task<ICDPSession> CreateCDPSessionAsync();
91+
92+
internal void AddChildTarget(CdpTarget target) => _childTargets.Add(target);
93+
94+
internal void RemoveChildTarget(CdpTarget target) => _childTargets.Remove(target);
8395
}
8496
}

0 commit comments

Comments
 (0)
Please sign in to comment.