Skip to content

Commit

Permalink
Validate execution context
Browse files Browse the repository at this point in the history
  • Loading branch information
kblok committed Nov 17, 2023
1 parent 717237f commit 6382a92
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 114 deletions.
94 changes: 42 additions & 52 deletions lib/PuppeteerSharp.Tests/HeadfulTests/HeadfulTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class HeadfulTests : PuppeteerBaseTest
{
private readonly LaunchOptions _forcedOopifOptions;

public HeadfulTests(): base()
public HeadfulTests()
{
_forcedOopifOptions = TestConstants.DefaultBrowserOptions();
_forcedOopifOptions.Headless = false;
Expand All @@ -29,9 +29,9 @@ public HeadfulTests(): base()
[Skip(SkipAttribute.Targets.Firefox)]
public async Task BackgroundPageTargetTypeShouldBeAvailable()
{
await using (var browserWithExtension = await Puppeteer.LaunchAsync(
await using var browserWithExtension = await Puppeteer.LaunchAsync(
TestConstants.BrowserWithExtensionOptions(),
TestConstants.LoggerFactory))
TestConstants.LoggerFactory);
await using (await browserWithExtension.NewPageAsync())
{
var backgroundPageTarget = await browserWithExtension.WaitForTargetAsync(t => t.Type == TargetType.BackgroundPage);
Expand All @@ -43,59 +43,51 @@ public async Task BackgroundPageTargetTypeShouldBeAvailable()
[Ignore("Marked as Fail/Pass upstream")]
public async Task TargetPageShouldReturnABackgroundPage()
{
await using (var browserWithExtension = await Puppeteer.LaunchAsync(
await using var browserWithExtension = await Puppeteer.LaunchAsync(
TestConstants.BrowserWithExtensionOptions(),
TestConstants.LoggerFactory))
{
var backgroundPageTarget = await browserWithExtension.WaitForTargetAsync(t => t.Type == TargetType.BackgroundPage);
await using (var page = await backgroundPageTarget.PageAsync())
{
Assert.AreEqual(6, await page.EvaluateFunctionAsync<int>("() => 2 * 3"));
Assert.AreEqual(42, await page.EvaluateFunctionAsync<int>("() => window.MAGIC"));
}
}
TestConstants.LoggerFactory);
var backgroundPageTarget = await browserWithExtension.WaitForTargetAsync(t => t.Type == TargetType.BackgroundPage);
await using var page = await backgroundPageTarget.PageAsync();
Assert.AreEqual(6, await page.EvaluateFunctionAsync<int>("() => 2 * 3"));
Assert.AreEqual(42, await page.EvaluateFunctionAsync<int>("() => window.MAGIC"));
}

[PuppeteerTest("headful.spec.ts", "HEADFUL", "should have default url when launching browser")]
[Skip(SkipAttribute.Targets.Firefox)]
public async Task ShouldHaveDefaultUrlWhenLaunchingBrowser()
{
await using (var browser = await Puppeteer.LaunchAsync(
await using var browser = await Puppeteer.LaunchAsync(
TestConstants.BrowserWithExtensionOptions(),
TestConstants.LoggerFactory))
{
var pages = (await browser.PagesAsync()).Select(page => page.Url).ToArray();
Assert.AreEqual(new[] { "about:blank" }, pages);
}
TestConstants.LoggerFactory);
var pages = (await browser.PagesAsync()).Select(page => page.Url).ToArray();
Assert.AreEqual(new[] { "about:blank" }, pages);
}

[PuppeteerTest("headful.spec.ts", "HEADFUL", "headless should be able to read cookies written by headful")]
[Ignore("Puppeteer ignores this in windows we do not have a platform filter yet")]
public async Task HeadlessShouldBeAbleToReadCookiesWrittenByHeadful()
{
using (var userDataDir = new TempDirectory())
using var userDataDir = new TempDirectory();
var launcher = new Launcher(TestConstants.LoggerFactory);
var options = TestConstants.DefaultBrowserOptions();
options.Args = options.Args.Concat(new[] { $"--user-data-dir=\"{userDataDir}\"" }).ToArray();
options.Headless = false;
await using (var browser = await launcher.LaunchAsync(options))
await using (var page = await browser.NewPageAsync())
{
var launcher = new Launcher(TestConstants.LoggerFactory);
var options = TestConstants.DefaultBrowserOptions();
options.Args = options.Args.Concat(new[] { $"--user-data-dir=\"{userDataDir}\"" }).ToArray();
options.Headless = false;
await using (var browser = await launcher.LaunchAsync(options))
await using (var page = await browser.NewPageAsync())
{
await page.GoToAsync(TestConstants.EmptyPage);
await page.EvaluateExpressionAsync(
"document.cookie = 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT'");
}
await page.GoToAsync(TestConstants.EmptyPage);
await page.EvaluateExpressionAsync(
"document.cookie = 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT'");
}

await TestUtils.WaitForCookieInChromiumFileAsync(userDataDir.Path, "foo");
await TestUtils.WaitForCookieInChromiumFileAsync(userDataDir.Path, "foo");

options.Headless = true;
await using (var browser2 = await Puppeteer.LaunchAsync(options, TestConstants.LoggerFactory))
{
var page2 = await browser2.NewPageAsync();
await page2.GoToAsync(TestConstants.EmptyPage);
Assert.AreEqual("foo=true", await page2.EvaluateExpressionAsync<string>("document.cookie"));
}
options.Headless = true;
await using (var browser2 = await Puppeteer.LaunchAsync(options, TestConstants.LoggerFactory))
{
var page2 = await browser2.NewPageAsync();
await page2.GoToAsync(TestConstants.EmptyPage);
Assert.AreEqual("foo=true", await page2.EvaluateExpressionAsync<string>("document.cookie"));
}
}

Expand All @@ -106,24 +98,22 @@ public async Task OOPIFShouldReportGoogleComFrame()
// https://google.com is isolated by default in Chromium embedder.
var headfulOptions = TestConstants.DefaultBrowserOptions();
headfulOptions.Headless = false;
await using (var browser = await Puppeteer.LaunchAsync(headfulOptions))
await using (var page = await browser.NewPageAsync())
{
await page.GoToAsync(TestConstants.EmptyPage);
await page.SetRequestInterceptionAsync(true);
page.Request += async (_, e) => await e.Request.RespondAsync(
new ResponseData { Body = "{ body: 'YO, GOOGLE.COM'}" });
await page.EvaluateFunctionHandleAsync(@"() => {
await using var browser = await Puppeteer.LaunchAsync(headfulOptions);
await using var page = await browser.NewPageAsync();
await page.GoToAsync(TestConstants.EmptyPage);
await page.SetRequestInterceptionAsync(true);
page.Request += async (_, e) => await e.Request.RespondAsync(
new ResponseData { Body = "{ body: 'YO, GOOGLE.COM'}" });
await page.EvaluateFunctionHandleAsync(@"() => {
const frame = document.createElement('iframe');
frame.setAttribute('src', 'https://google.com/');
document.body.appendChild(frame);
return new Promise(x => frame.onload = x);
}");
await page.WaitForSelectorAsync("iframe[src=\"https://google.com/\"]");
var urls = Array.ConvertAll(page.Frames, frame => frame.Url);
Array.Sort(urls);
Assert.AreEqual(new[] { TestConstants.EmptyPage, "https://google.com/" }, urls);
}
await page.WaitForSelectorAsync("iframe[src=\"https://google.com/\"]");
var urls = Array.ConvertAll(page.Frames, frame => frame.Url);
Array.Sort((Array)urls);
Assert.AreEqual(new[] { TestConstants.EmptyPage, "https://google.com/" }, urls);
}

[PuppeteerTest("headful.spec.ts", "HEADFUL", "OOPIF: should expose events within OOPIFs")]
Expand Down
35 changes: 13 additions & 22 deletions lib/PuppeteerSharp.Tests/HeadfulTests/PageBringToFrontTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using PuppeteerSharp.Helpers;
using PuppeteerSharp.Tests.Attributes;
using PuppeteerSharp.Nunit;
using NUnit.Framework;
Expand All @@ -10,33 +7,27 @@ namespace PuppeteerSharp.Tests.HeadfulTests
{
public class PageBringToFrontTests : PuppeteerBaseTest
{
public PageBringToFrontTests(): base()
{
}

[PuppeteerTest("headful.spec.ts", "Page.bringToFront", "should work")]
[Skip(SkipAttribute.Targets.Firefox)]
public async Task ShouldWork()
{
await using (var browserWithExtension = await Puppeteer.LaunchAsync(
await using var browserWithExtension = await Puppeteer.LaunchAsync(
TestConstants.BrowserWithExtensionOptions(),
TestConstants.LoggerFactory))
await using (var page = await browserWithExtension.NewPageAsync())
{
await page.GoToAsync(TestConstants.EmptyPage);
Assert.AreEqual("visible", await page.EvaluateExpressionAsync<string>("document.visibilityState"));
TestConstants.LoggerFactory);
await using var page = await browserWithExtension.NewPageAsync();
await page.GoToAsync(TestConstants.EmptyPage);
Assert.AreEqual("visible", await page.EvaluateExpressionAsync<string>("document.visibilityState"));

var newPage = await browserWithExtension.NewPageAsync();
await newPage.GoToAsync(TestConstants.EmptyPage);
Assert.AreEqual("hidden", await page.EvaluateExpressionAsync<string>("document.visibilityState"));
Assert.AreEqual("visible", await newPage.EvaluateExpressionAsync<string>("document.visibilityState"));
var newPage = await browserWithExtension.NewPageAsync();
await newPage.GoToAsync(TestConstants.EmptyPage);
Assert.AreEqual("hidden", await page.EvaluateExpressionAsync<string>("document.visibilityState"));
Assert.AreEqual("visible", await newPage.EvaluateExpressionAsync<string>("document.visibilityState"));

await page.BringToFrontAsync();
Assert.AreEqual("visible", await page.EvaluateExpressionAsync<string>("document.visibilityState"));
Assert.AreEqual("hidden", await newPage.EvaluateExpressionAsync<string>("document.visibilityState"));
await page.BringToFrontAsync();
Assert.AreEqual("visible", await page.EvaluateExpressionAsync<string>("document.visibilityState"));
Assert.AreEqual("hidden", await newPage.EvaluateExpressionAsync<string>("document.visibilityState"));

await newPage.CloseAsync();
}
await newPage.CloseAsync();
}
}
}
3 changes: 0 additions & 3 deletions lib/PuppeteerSharp/ExecutionContext.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using PuppeteerSharp.Helpers;
using PuppeteerSharp.Messaging;
using PuppeteerSharp.QueryHandlers;

namespace PuppeteerSharp
{
Expand Down
10 changes: 9 additions & 1 deletion lib/PuppeteerSharp/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,15 @@ internal Frame(FrameManager frameManager, string frameId, string parentFrameId,
internal bool HasStartedLoading { get; private set; }

/// <inheritdoc/>
public Task<IResponse> GoToAsync(string url, NavigationOptions options) => FrameManager.NavigateFrameAsync(this, url, options);
public Task<IResponse> GoToAsync(string url, NavigationOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}

return FrameManager.NavigateFrameAsync(this, url, options);
}

/// <inheritdoc/>
public Task<IResponse> GoToAsync(string url, int? timeout = null, WaitUntilNavigation[] waitUntil = null)
Expand Down
71 changes: 35 additions & 36 deletions lib/PuppeteerSharp/FrameManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using PuppeteerSharp.Helpers;
using PuppeteerSharp.Helpers.Json;
using PuppeteerSharp.Messaging;
using static System.Collections.Specialized.BitVector32;

namespace PuppeteerSharp
{
Expand Down Expand Up @@ -57,7 +56,7 @@ internal FrameManager(CDPSession client, Page page, bool ignoreHTTPSErrors, Time

internal TimeoutSettings TimeoutSettings { get; }

internal FrameTree FrameTree { get; private set; } = new();
internal FrameTree FrameTree { get; } = new();

public async Task<IResponse> NavigateFrameAsync(Frame frame, string url, NavigationOptions options)
{
Expand All @@ -67,48 +66,44 @@ public async Task<IResponse> NavigateFrameAsync(Frame frame, string url, Navigat
var referrerPolicy = string.IsNullOrEmpty(options.ReferrerPolicy)
? NetworkManager.ExtraHTTPHeaders?.GetValueOrDefault("referer-policy")
: options.ReferrerPolicy;
var timeout = options?.Timeout ?? TimeoutSettings.NavigationTimeout;
var timeout = options.Timeout ?? TimeoutSettings.NavigationTimeout;

using (var watcher = new LifecycleWatcher(this, frame, options?.WaitUntil, timeout))
using var watcher = new LifecycleWatcher(this, frame, options.WaitUntil, timeout);
try
{
try
{
var navigateTask = NavigateAsync(Client, url, referrer, referrerPolicy, frame.Id);
var task = await Task.WhenAny(
watcher.TimeoutOrTerminationTask,
navigateTask).ConfigureAwait(false);
var navigateTask = NavigateAsync(Client, url, referrer, referrerPolicy, frame.Id);
var task = await Task.WhenAny(
watcher.TimeoutOrTerminationTask,
navigateTask).ConfigureAwait(false);

await task.ConfigureAwait(false);
await task.ConfigureAwait(false);

task = await Task.WhenAny(
watcher.TimeoutOrTerminationTask,
_ensureNewDocumentNavigation ? watcher.NewDocumentNavigationTask : watcher.SameDocumentNavigationTask).ConfigureAwait(false);
task = await Task.WhenAny(
watcher.TimeoutOrTerminationTask,
_ensureNewDocumentNavigation ? watcher.NewDocumentNavigationTask : watcher.SameDocumentNavigationTask).ConfigureAwait(false);

await task.ConfigureAwait(false);
}
catch (Exception ex)
{
throw new NavigationException(ex.Message, ex);
}

return watcher.NavigationResponse;
await task.ConfigureAwait(false);
}
catch (Exception ex)
{
throw new NavigationException(ex.Message, ex);
}

return watcher.NavigationResponse;
}

public async Task<IResponse> WaitForFrameNavigationAsync(Frame frame, NavigationOptions options = null)
{
var timeout = options?.Timeout ?? TimeoutSettings.NavigationTimeout;
using (var watcher = new LifecycleWatcher(this, frame, options?.WaitUntil, timeout))
{
var raceTask = await Task.WhenAny(
watcher.NewDocumentNavigationTask,
watcher.SameDocumentNavigationTask,
watcher.TimeoutOrTerminationTask).ConfigureAwait(false);
using var watcher = new LifecycleWatcher(this, frame, options?.WaitUntil, timeout);
var raceTask = await Task.WhenAny(
watcher.NewDocumentNavigationTask,
watcher.SameDocumentNavigationTask,
watcher.TimeoutOrTerminationTask).ConfigureAwait(false);

await raceTask.ConfigureAwait(false);
await raceTask.ConfigureAwait(false);

return watcher.NavigationResponse;
}
return watcher.NavigationResponse;
}

internal async Task InitializeAsync(CDPSession client = null)
Expand Down Expand Up @@ -182,8 +177,6 @@ internal void OnAttachedToTarget(TargetChangedArgs e)
_ = InitializeAsync(e.Target.Session);
}

internal Frame GetFrame(string frameid) => FrameTree.GetById(frameid);

internal Frame[] GetFrames() => FrameTree.Frames;

internal ExecutionContext GetExecutionContextById(int contextId, CDPSession session)
Expand All @@ -192,6 +185,8 @@ internal ExecutionContext GetExecutionContextById(int contextId, CDPSession sess
return context;
}

private Frame GetFrame(string frameId) => FrameTree.GetById(frameId);

private async Task NavigateAsync(CDPSession client, string url, string referrer, string referrerPolicy, string frameId)
{
var response = await client.SendAsync<PageNavigateResponse>("Page.navigate", new PageNavigateRequest
Expand Down Expand Up @@ -255,8 +250,6 @@ private void Client_MessageReceived(object sender, MessageEventArgs e)
case "Page.lifecycleEvent":
OnLifeCycleEvent(e.MessageData.ToObject<LifecycleEventResponse>(true));
break;
default:
break;
}
}
catch (Exception ex)
Expand Down Expand Up @@ -319,7 +312,7 @@ private void OnExecutionContextDestroyed(int contextId, CDPSession session)
}
}

private async Task OnExecutionContextCreatedAsync(ContextPayload contextPayload, CDPSession session)
private async Task OnExecutionContextCreatedAsync(ContextPayload contextPayload, ICDPSession session)
{
var frameId = contextPayload.AuxData?.FrameId;
var frame = !string.IsNullOrEmpty(frameId) ? await FrameTree.GetFrameAsync(frameId).ConfigureAwait(false) : null;
Expand All @@ -345,6 +338,12 @@ private async Task OnExecutionContextCreatedAsync(ContextPayload contextPayload,
}
}

// If there is no world, the context is not meant to be handled by us.
if (world == null)
{
return;
}

var context = new ExecutionContext(frame?.Client ?? Client, contextPayload, world);
world?.SetContext(context);

Expand Down
7 changes: 7 additions & 0 deletions lib/PuppeteerSharp/Page.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1675,6 +1675,13 @@ private Task OnConsoleAPIAsync(PageConsoleResponse message)
}

var ctx = FrameManager.ExecutionContextById(message.ExecutionContextId, Client);

if (ctx == null)
{
_logger.LogError($"ExecutionContext not found from message.");
return Task.CompletedTask;
}

var values = message.Args.Select(ctx.CreateJSHandle).ToArray();

return AddConsoleMessageAsync(message.Type, values, message.StackTrace);
Expand Down

0 comments on commit 6382a92

Please sign in to comment.