diff --git a/lib/PuppeteerSharp.Tests/PageTests/NewPageTests.cs b/lib/PuppeteerSharp.Tests/PageTests/NewPageTests.cs new file mode 100644 index 000000000..df21b1754 --- /dev/null +++ b/lib/PuppeteerSharp.Tests/PageTests/NewPageTests.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using PuppeteerSharp.Nunit; + +namespace PuppeteerSharp.Tests.PageTests +{ + public class NewPageTests : PuppeteerPageBaseTest + { + [Test, PuppeteerTest("page.spec", "Page Page.newPage", "should create a background page")] + public async Task ShouldCreateABackgroundPage() + { + var page = await Context.NewPageAsync(new CreatePageOptions { Background = true }); + + var visibilityState = await page.EvaluateExpressionAsync("document.visibilityState"); + Assert.That(visibilityState, Is.EqualTo("hidden")); + } + } +} diff --git a/lib/PuppeteerSharp/Bidi/BidiBrowser.cs b/lib/PuppeteerSharp/Bidi/BidiBrowser.cs index 821306b37..d99fb1a85 100644 --- a/lib/PuppeteerSharp/Bidi/BidiBrowser.cs +++ b/lib/PuppeteerSharp/Bidi/BidiBrowser.cs @@ -174,7 +174,7 @@ public override async Task CloseAsync() } /// - public override Task NewPageAsync() => DefaultContext.NewPageAsync(); + public override Task NewPageAsync(CreatePageOptions options = null) => DefaultContext.NewPageAsync(options); /// public override Task ScreensAsync() diff --git a/lib/PuppeteerSharp/Bidi/BidiBrowserContext.cs b/lib/PuppeteerSharp/Bidi/BidiBrowserContext.cs index 9be34abd4..0cb92a4a6 100644 --- a/lib/PuppeteerSharp/Bidi/BidiBrowserContext.cs +++ b/lib/PuppeteerSharp/Bidi/BidiBrowserContext.cs @@ -121,9 +121,15 @@ public override async Task ClearPermissionOverridesAsync() public override Task PagesAsync() => Task.FromResult(_pages.Values.Cast().ToArray()); /// - public override async Task NewPageAsync() + public override async Task NewPageAsync(CreatePageOptions options = null) { - var context = await UserContext.CreateBrowserContextAsync(WebDriverBiDi.BrowsingContext.CreateType.Tab).ConfigureAwait(false); + var type = options?.Type == "window" + ? WebDriverBiDi.BrowsingContext.CreateType.Window + : WebDriverBiDi.BrowsingContext.CreateType.Tab; + + var context = await UserContext.CreateBrowserContextAsync( + type, + background: options?.Background).ConfigureAwait(false); if (!_pages.TryGetValue(context, out var page)) { diff --git a/lib/PuppeteerSharp/Bidi/Core/UserContext.cs b/lib/PuppeteerSharp/Bidi/Core/UserContext.cs index c416ad558..ce45bcdd8 100644 --- a/lib/PuppeteerSharp/Bidi/Core/UserContext.cs +++ b/lib/PuppeteerSharp/Bidi/Core/UserContext.cs @@ -77,9 +77,13 @@ public void Dispose() GC.SuppressFinalize(this); } - public async Task CreateBrowserContextAsync(CreateType contextType) + public async Task CreateBrowserContextAsync(CreateType contextType, bool? background = null) { - var createParams = new CreateCommandParameters(contextType) { UserContextId = Id }; + var createParams = new CreateCommandParameters(contextType) + { + UserContextId = Id, + IsCreatedInBackground = background, + }; var result = await Session.Driver.BrowsingContext.CreateAsync(createParams).ConfigureAwait(false); if (_browsingContexts.TryGetValue(result.BrowsingContextId, out var browsingContext)) diff --git a/lib/PuppeteerSharp/Browser.cs b/lib/PuppeteerSharp/Browser.cs index 853decdcd..18d6286d9 100644 --- a/lib/PuppeteerSharp/Browser.cs +++ b/lib/PuppeteerSharp/Browser.cs @@ -71,7 +71,7 @@ public abstract class Browser : IBrowser internal abstract ProtocolType Protocol { get; } /// - public abstract Task NewPageAsync(); + public abstract Task NewPageAsync(CreatePageOptions options = null); /// public abstract ITarget[] Targets(); diff --git a/lib/PuppeteerSharp/BrowserContext.cs b/lib/PuppeteerSharp/BrowserContext.cs index e1587e261..77e91058f 100644 --- a/lib/PuppeteerSharp/BrowserContext.cs +++ b/lib/PuppeteerSharp/BrowserContext.cs @@ -40,7 +40,7 @@ public Task WaitForTargetAsync(Func predicate, WaitForOp public abstract Task PagesAsync(); /// - public abstract Task NewPageAsync(); + public abstract Task NewPageAsync(CreatePageOptions options = null); /// public abstract Task CloseAsync(); diff --git a/lib/PuppeteerSharp/Cdp/CdpBrowser.cs b/lib/PuppeteerSharp/Cdp/CdpBrowser.cs index 78bb211d3..bdace08a8 100644 --- a/lib/PuppeteerSharp/Cdp/CdpBrowser.cs +++ b/lib/PuppeteerSharp/Cdp/CdpBrowser.cs @@ -103,7 +103,7 @@ public override bool IsClosed internal override ProtocolType Protocol => ProtocolType.Cdp; /// - public override Task NewPageAsync() => DefaultContext.NewPageAsync(); + public override Task NewPageAsync(CreatePageOptions options = null) => DefaultContext.NewPageAsync(options); /// public override ITarget[] Targets() @@ -205,11 +205,23 @@ internal static async Task CreateAsync( } } - internal async Task CreatePageInContextAsync(string contextId) + internal async Task CreatePageInContextAsync(string contextId, CreatePageOptions options = null) { + var hasTargets = Array.Exists(Targets(), t => t.BrowserContext.Id == contextId); + var windowBounds = options?.Type == "window" ? options.WindowBounds : null; + var createTargetRequest = new TargetCreateTargetRequest { Url = "about:blank", + Left = windowBounds?.Left, + Top = windowBounds?.Top, + Width = windowBounds?.Width, + Height = windowBounds?.Height, + WindowState = windowBounds?.WindowState, + + // Works around crbug.com/454825274. + NewWindow = hasTargets && options?.Type == "window" ? true : null, + Background = options?.Background, }; if (contextId != null) diff --git a/lib/PuppeteerSharp/Cdp/CdpBrowserContext.cs b/lib/PuppeteerSharp/Cdp/CdpBrowserContext.cs index 38674bba9..8e8282ed1 100644 --- a/lib/PuppeteerSharp/Cdp/CdpBrowserContext.cs +++ b/lib/PuppeteerSharp/Cdp/CdpBrowserContext.cs @@ -56,7 +56,7 @@ public override async Task PagesAsync() .Where(p => p != null).ToArray(); /// - public override Task NewPageAsync() => _browser.CreatePageInContextAsync(Id); + public override Task NewPageAsync(CreatePageOptions options = null) => _browser.CreatePageInContextAsync(Id, options); /// public override Task CloseAsync() diff --git a/lib/PuppeteerSharp/Cdp/Messaging/TargetCreateTargetRequest.cs b/lib/PuppeteerSharp/Cdp/Messaging/TargetCreateTargetRequest.cs index 17580bb93..284f9c0f8 100644 --- a/lib/PuppeteerSharp/Cdp/Messaging/TargetCreateTargetRequest.cs +++ b/lib/PuppeteerSharp/Cdp/Messaging/TargetCreateTargetRequest.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace PuppeteerSharp.Cdp.Messaging { internal class TargetCreateTargetRequest @@ -5,5 +7,26 @@ internal class TargetCreateTargetRequest public string Url { get; set; } public object BrowserContextId { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Left { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Top { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Width { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Height { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public WindowState? WindowState { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? NewWindow { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? Background { get; set; } } } diff --git a/lib/PuppeteerSharp/CreatePageOptions.cs b/lib/PuppeteerSharp/CreatePageOptions.cs new file mode 100644 index 000000000..4981136f4 --- /dev/null +++ b/lib/PuppeteerSharp/CreatePageOptions.cs @@ -0,0 +1,25 @@ +namespace PuppeteerSharp +{ + /// + /// Options for creating a new page. + /// + public class CreatePageOptions + { + /// + /// Gets or sets the type of page to create. + /// Use "tab" (default) or "window". + /// + public string Type { get; set; } + + /// + /// Gets or sets the window bounds when is "window". + /// + public WindowBounds WindowBounds { get; set; } + + /// + /// Gets or sets a value indicating whether to create the page in the background. + /// + /// false by default. + public bool? Background { get; set; } + } +} diff --git a/lib/PuppeteerSharp/IBrowser.cs b/lib/PuppeteerSharp/IBrowser.cs index ffb6c34d6..7a90dc723 100644 --- a/lib/PuppeteerSharp/IBrowser.cs +++ b/lib/PuppeteerSharp/IBrowser.cs @@ -171,8 +171,9 @@ public interface IBrowser : IDisposable, IAsyncDisposable /// /// Creates a new page. /// + /// Options for creating the page. /// Task which resolves to a new object. - Task NewPageAsync(); + Task NewPageAsync(CreatePageOptions options = null); /// /// Returns a Task which resolves to an array of all open pages. diff --git a/lib/PuppeteerSharp/IBrowserContext.cs b/lib/PuppeteerSharp/IBrowserContext.cs index 3b60dbe6a..3b7b0dc4b 100644 --- a/lib/PuppeteerSharp/IBrowserContext.cs +++ b/lib/PuppeteerSharp/IBrowserContext.cs @@ -55,8 +55,9 @@ public interface IBrowserContext : IDisposable, IAsyncDisposable /// /// Creates a new page. /// + /// Options for creating the page. /// Task which resolves to a new object. - Task NewPageAsync(); + Task NewPageAsync(CreatePageOptions options = null); /// /// Overrides the browser context permissions. diff --git a/lib/PuppeteerSharp/WindowBounds.cs b/lib/PuppeteerSharp/WindowBounds.cs new file mode 100644 index 000000000..57ddf7106 --- /dev/null +++ b/lib/PuppeteerSharp/WindowBounds.cs @@ -0,0 +1,33 @@ +namespace PuppeteerSharp +{ + /// + /// Bounds for a browser window. + /// + public class WindowBounds + { + /// + /// Gets or sets the left position of the window. + /// + public int? Left { get; set; } + + /// + /// Gets or sets the top position of the window. + /// + public int? Top { get; set; } + + /// + /// Gets or sets the width of the window. + /// + public int? Width { get; set; } + + /// + /// Gets or sets the height of the window. + /// + public int? Height { get; set; } + + /// + /// Gets or sets the window state. + /// + public WindowState? WindowState { get; set; } + } +} diff --git a/lib/PuppeteerSharp/WindowState.cs b/lib/PuppeteerSharp/WindowState.cs new file mode 100644 index 000000000..8c20a0a2b --- /dev/null +++ b/lib/PuppeteerSharp/WindowState.cs @@ -0,0 +1,37 @@ +using System.Runtime.Serialization; +using System.Text.Json.Serialization; +using PuppeteerSharp.Helpers.Json; + +namespace PuppeteerSharp +{ + /// + /// Window state for . + /// + [JsonConverter(typeof(JsonStringEnumMemberConverter))] + public enum WindowState + { + /// + /// Normal window state. + /// + [EnumMember(Value = "normal")] + Normal, + + /// + /// Minimized window state. + /// + [EnumMember(Value = "minimized")] + Minimized, + + /// + /// Maximized window state. + /// + [EnumMember(Value = "maximized")] + Maximized, + + /// + /// Fullscreen window state. + /// + [EnumMember(Value = "fullscreen")] + Fullscreen, + } +}