Skip to content

Commit ca42f10

Browse files
authored
Execute frame.waitFor{Selector,XPath} in secondary world (#936)
1 parent 2e59b1e commit ca42f10

File tree

9 files changed

+78
-28
lines changed

9 files changed

+78
-28
lines changed

lib/PuppeteerSharp.Tests/FrameTests/WaitForSelectorTests.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ public async Task ShouldImmediatelyResolveTaskIfNodeExists()
2424
await frame.WaitForSelectorAsync("div");
2525
}
2626

27+
[Fact]
28+
public async Task ShouldWorkWithRemovedMutationObserver()
29+
{
30+
await Page.EvaluateExpressionAsync("delete window.MutationObserver");
31+
var waitForSelector = Page.WaitForSelectorAsync(".zombo");
32+
await Page.SetContentAsync("<div class='zombo'>anything</div>");
33+
Assert.Equal("anything", await Page.EvaluateFunctionAsync<string>("x => x.textContent", await waitForSelector));
34+
}
35+
2736
[Fact]
2837
public async Task ShouldResolveTaskWhenNodeIsAdded()
2938
{
@@ -75,17 +84,6 @@ public async Task ShouldRunInSpecifiedFrame()
7584
Assert.Equal(frame2, eHandle.ExecutionContext.Frame);
7685
}
7786

78-
[Fact]
79-
public async Task ShouldThrowIfEvaluationFailed()
80-
{
81-
await Page.EvaluateOnNewDocumentAsync(@"function() {
82-
document.querySelector = null;
83-
}");
84-
await Page.GoToAsync(TestConstants.EmptyPage);
85-
var exception = await Assert.ThrowsAnyAsync<PuppeteerException>(() => Page.WaitForSelectorAsync("*"));
86-
Assert.Contains("document.querySelector is not a function", exception.Message);
87-
}
88-
8987
[Fact]
9088
public async Task ShouldThrowWhenFrameIsDetached()
9189
{

lib/PuppeteerSharp.Tests/FrameTests/WaitForXPathTests.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,6 @@ public async Task ShouldRunInSpecifiedFrame()
3838
Assert.Equal(frame2, eHandle.ExecutionContext.Frame);
3939
}
4040

41-
[Fact]
42-
public async Task ShouldThrowIfEvaluationFailed()
43-
{
44-
await Page.EvaluateOnNewDocumentAsync(@"function() {
45-
document.evaluate = null;
46-
}");
47-
await Page.GoToAsync(TestConstants.EmptyPage);
48-
var exception = await Assert.ThrowsAsync<EvaluationFailedException>(()
49-
=> Page.WaitForXPathAsync("*"));
50-
Assert.Contains("document.evaluate is not a function", exception.Message);
51-
}
52-
5341
[Fact]
5442
public async Task ShouldThrowWhenFrameIsDetached()
5543
{

lib/PuppeteerSharp/ExecutionContext.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,5 +266,30 @@ private static string GetExceptionMessage(EvaluateExceptionResponseDetails excep
266266
}
267267
return message;
268268
}
269+
270+
internal async Task<ElementHandle> AdoptElementHandleASync(ElementHandle elementHandle)
271+
{
272+
if (elementHandle.ExecutionContext == this)
273+
{
274+
throw new PuppeteerException("Cannot adopt handle that already belongs to this execution context");
275+
}
276+
if (World == null)
277+
{
278+
throw new PuppeteerException("Cannot adopt handle without DOMWorld");
279+
}
280+
281+
var nodeInfo = await _client.SendAsync<DomDescribeNodeResponse>("DOM.describeNode", new DomDescribeNodeRequest
282+
{
283+
ObjectId = elementHandle.RemoteObject.ObjectId,
284+
}).ConfigureAwait(false);
285+
286+
var obj = await _client.SendAsync<DomResolveNodeResponse>("DOM.resolveNode", new DomResolveNodeRequest
287+
{
288+
BackendNodeId = nodeInfo.Node.BackendNodeId,
289+
ExecutionContextId = _contextId
290+
}).ConfigureAwait(false);
291+
292+
return CreateJSHandle(obj.Object) as ElementHandle;
293+
}
269294
}
270295
}

lib/PuppeteerSharp/Frame.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,18 @@ public Task<Response> GoToAsync(string url, int? timeout = null, WaitUntilNaviga
246246
/// <seealso cref="WaitForXPathAsync(string, WaitForSelectorOptions)"/>
247247
/// <seealso cref="Page.WaitForSelectorAsync(string, WaitForSelectorOptions)"/>
248248
/// <exception cref="WaitTaskTimeoutException">If timeout occurred.</exception>
249-
public Task<ElementHandle> WaitForSelectorAsync(string selector, WaitForSelectorOptions options = null)
250-
=> MainWorld.WaitForSelectorAsync(selector, options);
249+
public async Task<ElementHandle> WaitForSelectorAsync(string selector, WaitForSelectorOptions options = null)
250+
{
251+
var handle = await SecondaryWorld.WaitForSelectorAsync(selector, options).ConfigureAwait(false);
252+
if (handle == null)
253+
{
254+
return null;
255+
}
256+
var mainExecutionContext = await MainWorld.GetExecutionContextAsync().ConfigureAwait(false);
257+
var result = await mainExecutionContext.AdoptElementHandleASync(handle).ConfigureAwait(false);
258+
await handle.DisposeAsync().ConfigureAwait(false);
259+
return result;
260+
}
251261

252262
/// <summary>
253263
/// Waits for a selector to be added to the DOM
@@ -277,8 +287,18 @@ public Task<ElementHandle> WaitForSelectorAsync(string selector, WaitForSelector
277287
/// <seealso cref="WaitForSelectorAsync(string, WaitForSelectorOptions)"/>
278288
/// <seealso cref="Page.WaitForXPathAsync(string, WaitForSelectorOptions)"/>
279289
/// <exception cref="WaitTaskTimeoutException">If timeout occurred.</exception>
280-
public Task<ElementHandle> WaitForXPathAsync(string xpath, WaitForSelectorOptions options = null)
281-
=> MainWorld.WaitForXPathAsync(xpath, options);
290+
public async Task<ElementHandle> WaitForXPathAsync(string xpath, WaitForSelectorOptions options = null)
291+
{
292+
var handle = await SecondaryWorld.WaitForXPathAsync(xpath, options).ConfigureAwait(false);
293+
if (handle == null)
294+
{
295+
return null;
296+
}
297+
var mainExecutionContext = await MainWorld.GetExecutionContextAsync().ConfigureAwait(false);
298+
var result = await mainExecutionContext.AdoptElementHandleASync(handle).ConfigureAwait(false);
299+
await handle.DisposeAsync().ConfigureAwait(false);
300+
return result;
301+
}
282302

283303
/// <summary>
284304
/// Waits for a timeout

lib/PuppeteerSharp/Messaging/DomDescribeNodeResponse.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ internal class DomDescribeNodeResponse
77
internal class DomNode
88
{
99
public string FrameId { get; set; }
10+
public int BackendNodeId { get; set; }
1011
}
1112
}
1213
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace PuppeteerSharp.Messaging
2+
{
3+
internal class DomResolveNodeRequest
4+
{
5+
public int BackendNodeId { get; set; }
6+
public int ExecutionContextId { get; set; }
7+
}
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace PuppeteerSharp.Messaging
2+
{
3+
internal class DomResolveNodeResponse
4+
{
5+
public RemoteObject Object { get; set; }
6+
}
7+
}

lib/PuppeteerSharp/Page.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1549,12 +1549,14 @@ await Task.WhenAll(
15491549
Enabled = true
15501550
}),
15511551
client.SendAsync("Network.enable", null),
1552-
client.SendAsync("Runtime.enable", null).ContinueWith(t => page.FrameManager.EnsureSecondaryDOMWorldAsync()),
1552+
client.SendAsync("Runtime.enable", null),
15531553
client.SendAsync("Security.enable", null),
15541554
client.SendAsync("Performance.enable", null),
15551555
client.SendAsync("Log.enable", null)
15561556
).ConfigureAwait(false);
15571557

1558+
await page.FrameManager.EnsureSecondaryDOMWorldAsync().ConfigureAwait(false);
1559+
15581560
if (ignoreHTTPSErrors)
15591561
{
15601562
await client.SendAsync("Security.setOverrideCertificateErrors", new SecuritySetOverrideCertificateErrorsRequest

lib/PuppeteerSharp/WaitTask.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ await _world.EvaluateFunctionAsync<bool>("s => !s", success)
194194

195195
if (exception?.Message.Contains("Execution context was destroyed") == true)
196196
{
197+
_ = Rerun();
197198
return;
198199
}
199200

0 commit comments

Comments
 (0)