Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1522,21 +1522,6 @@
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[page.spec] *Page.setContent*",
"platforms": [
"darwin",
"linux",
"win32"
],
"parameters": [
"webDriverBiDi"
],
"expectations": [
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[proxy.spec] *",
Expand Down
6 changes: 3 additions & 3 deletions lib/PuppeteerSharp.Tests/PageTests/SetContentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace PuppeteerSharp.Tests.PageTests
{
public class SetContentTests : PuppeteerPageBaseTest
{
const string ExpectedOutput = "<html><head></head><body><div>hello</div></body></html>";
private const string ExpectedOutput = "<html><head></head><body><div>hello</div></body></html>";

public async Task Usage(IBrowser browser)
{
Expand Down Expand Up @@ -65,7 +65,7 @@ public async Task ShouldRespectTimeout()
Timeout = 1
}));

Assert.That(exception.Message, Does.Contain("Timeout of 1 ms exceeded"));
Assert.That(exception!.Message, Does.Contain("Timeout of 1 ms exceeded"));
}

[Test, PuppeteerTest("page.spec", "Page Page.setContent", "should respect default navigation timeout")]
Expand All @@ -79,7 +79,7 @@ public async Task ShouldRespectDefaultTimeout()
var exception = Assert.ThrowsAsync<TimeoutException>(async () =>
await Page.SetContentAsync($"<img src='{TestConstants.ServerUrl + imgPath}'></img>"));

Assert.That(exception.Message, Does.Contain("Timeout of 1 ms exceeded"));
Assert.That(exception!.Message, Does.Contain("Timeout of 1 ms exceeded"));
}

[Test, PuppeteerTest("page.spec", "Page Page.setContent", "should await resources to load")]
Expand Down
11 changes: 9 additions & 2 deletions lib/PuppeteerSharp.Tests/PuppeteerBrowserBaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ public async Task InitializeAsync()
[TearDown]
public async Task TearDownAsync()
{
if (Browser is not null)
try
{
await Browser.DisposeAsync();
if (Browser is not null)
{
await Browser.DisposeAsync();
}
}
catch
{
// Ignore exceptions during browser disposal
}
}
}
Expand Down
7 changes: 3 additions & 4 deletions lib/PuppeteerSharp/Bidi/BidiElementHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
// * SOFTWARE.

using System.Threading.Tasks;
using PuppeteerSharp.Cdp.Messaging;
using PuppeteerSharp.QueryHandlers;
using WebDriverBiDi.Script;

Expand All @@ -40,15 +39,15 @@ internal class BidiElementHandle(RemoteValue value, BidiRealm realm) : ElementHa

internal override CustomQuerySelectorRegistry CustomQuerySelectorRegistry { get; } = new();

protected override Page Page { get; }
internal BidiFrame BidiFrame => realm.Environment as BidiFrame;

protected override Page Page => BidiFrame.BidiPage;

public static IJSHandle From(RemoteValue value, BidiRealm realm)
{
return new BidiElementHandle(value, realm);
}

public override ValueTask DisposeAsync() => throw new System.NotImplementedException();

public override Task UploadFileAsync(bool resolveFilePaths, params string[] filePaths) => throw new System.NotImplementedException();

public override Task<IFrame> ContentFrameAsync() => throw new System.NotImplementedException();
Expand Down
20 changes: 19 additions & 1 deletion lib/PuppeteerSharp/Bidi/BidiFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,25 @@ internal BidiPage BidiPage
public override Task<IElementHandle> AddScriptTagAsync(AddTagOptions options) => throw new System.NotImplementedException();

/// <inheritdoc />
public override Task SetContentAsync(string html, NavigationOptions options = null) => throw new System.NotImplementedException();
public override async Task SetContentAsync(string html, NavigationOptions options = null)
{
var timeout = options?.Timeout ?? TimeoutSettings.NavigationTimeout;

// Wait for load and network idle events
var waitForLoadTask = WaitForLoadAsync(options);
var waitForNetworkIdleTask = WaitForNetworkIdleAsync(options);

// Set the frame content using JavaScript, similar to CDP implementation
await EvaluateFunctionAsync(
@"html => {
document.open();
document.write(html);
document.close();
}",
html).ConfigureAwait(false);

await Task.WhenAll(waitForLoadTask, waitForNetworkIdleTask).WithTimeout(timeout).ConfigureAwait(false);
}

/// <inheritdoc />
public override async Task<IResponse> GoToAsync(string url, NavigationOptions options)
Expand Down
35 changes: 25 additions & 10 deletions lib/PuppeteerSharp/Bidi/BidiFrameRealm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@
// * SOFTWARE.

using System.Threading.Tasks;
using PuppeteerSharp.Helpers;

namespace PuppeteerSharp.Bidi;

internal class BidiFrameRealm(WindowRealm realm, BidiFrame frame) : BidiRealm(realm, frame.TimeoutSettings)
{
private readonly WindowRealm _realm = realm;
private bool _bindingsInstalled;
private readonly TaskQueue _puppeteerUtilQueue = new();
private IJSHandle _puppeteerUtil;

internal override IEnvironment Environment => frame;

internal BidiFrame Frame => frame;

public static BidiFrameRealm From(WindowRealm realm, BidiFrame frame)
{
Expand All @@ -38,17 +44,26 @@ public static BidiFrameRealm From(WindowRealm realm, BidiFrame frame)

public override async Task<IJSHandle> GetPuppeteerUtilAsync()
{
var installTcs = new TaskCompletionSource<bool>();
var scriptInjector = frame.BrowsingContext.Session.ScriptInjector;

if (!_bindingsInstalled)
await _puppeteerUtilQueue.Enqueue(async () =>
{
// TODO: Implement
installTcs.TrySetResult(true);
_bindingsInstalled = true;
}
if (_puppeteerUtil == null)
{
await scriptInjector.InjectAsync(
async (script) =>
{
if (_puppeteerUtil != null)
{
await _puppeteerUtil.DisposeAsync().ConfigureAwait(false);
}

await installTcs.Task.ConfigureAwait(false);
return await base.GetPuppeteerUtilAsync().ConfigureAwait(false);
_puppeteerUtil = await EvaluateExpressionHandleAsync(script).ConfigureAwait(false);
},
_puppeteerUtil == null).ConfigureAwait(false);
}
}).ConfigureAwait(false);
return _puppeteerUtil;
}

protected override void Initialize()
Expand All @@ -58,7 +73,7 @@ protected override void Initialize()
_realm.Updated += (_, __) =>
{
(Environment as Frame)?.ClearDocumentHandle();
_bindingsInstalled = false;
_puppeteerUtil = null;
};
}
}
18 changes: 16 additions & 2 deletions lib/PuppeteerSharp/Bidi/BidiJSHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ namespace PuppeteerSharp.Bidi;

internal class BidiJSHandle(RemoteValue value, BidiRealm realm) : JSHandle
{
private bool _disposed;

public RemoteValue RemoteValue { get; } = value;

public bool IsPrimitiveValue
Expand All @@ -42,6 +44,8 @@ public bool IsPrimitiveValue
}
}

internal string Id => RemoteValue.Handle;

internal override Realm Realm { get; } = realm;

public static BidiJSHandle From(RemoteValue value, BidiRealm realm)
Expand All @@ -51,8 +55,6 @@ public static BidiJSHandle From(RemoteValue value, BidiRealm realm)

public override Task<T> JsonValueAsync<T>() => throw new System.NotImplementedException();

public override ValueTask DisposeAsync() => throw new System.NotImplementedException();

/// <inheritdoc/>
public override string ToString()
{
Expand All @@ -63,4 +65,16 @@ public override string ToString()

return "JSHandle@" + RemoteValue.Type;
}

/// <inheritdoc/>
public override async ValueTask DisposeAsync()
{
if (_disposed)
{
return;
}

_disposed = true;
await realm.DestroyHandlesAsync(this).ConfigureAwait(false);
}
}
81 changes: 64 additions & 17 deletions lib/PuppeteerSharp/Bidi/BidiRealm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@

namespace PuppeteerSharp.Bidi;

/// <summary>
/// A BidiLazyArg is an evaluation argument that will be resolved when the CDP call is built.
/// </summary>
/// <param name="context">Execution context.</param>
/// <returns>Resolved argument.</returns>
public delegate Task<LocalValue> BidiLazyArg(IPuppeteerUtilWrapper context);

internal class BidiRealm(Core.Realm realm, TimeoutSettings timeoutSettings) : Realm(timeoutSettings), IDisposable, IPuppeteerUtilWrapper
{
private static readonly Regex _sourceUrlRegex = new(@"^[\x20\t]*//([@#])\s*sourceURL=\s{0,10}(\S*?)\s{0,10}$", RegexOptions.Multiline);
Expand All @@ -61,17 +54,70 @@ public void Dispose()

public virtual Task<IJSHandle> GetPuppeteerUtilAsync() => throw new NotImplementedException();

internal override Task<IJSHandle> AdoptHandleAsync(IJSHandle handle) => throw new System.NotImplementedException();
public async Task DestroyHandlesAsync(params BidiJSHandle[] handles)
{
if (Disposed)
{
return;
}

var handleIds = handles
.Select((handle) => handle.Id)
.Where(id => !string.IsNullOrEmpty(id)).ToArray();

if (handleIds.Length == 0)
{
return;
}

try
{
await realm.DisownAsync(handleIds).ConfigureAwait(false);
}
catch
{
// TODO: Add Logger
}
}

internal override async Task<IJSHandle> AdoptHandleAsync(IJSHandle handle)
{
try
{
return await EvaluateFunctionHandleAsync(
@"node => {
return node;
}",
handle).ConfigureAwait(false);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}

internal override Task<IElementHandle> AdoptBackendNodeAsync(object backendNodeId) => throw new System.NotImplementedException();

internal override Task<IJSHandle> TransferHandleAsync(IJSHandle handle) => throw new System.NotImplementedException();
internal override async Task<IJSHandle> TransferHandleAsync(IJSHandle handle)
{
var handleImpl = handle as JSHandle;

if (handleImpl.Realm == this)
{
return handle;
}

var transferredHandle = AdoptHandleAsync(handle);
await handle.DisposeAsync().ConfigureAwait(false);
return await transferredHandle.ConfigureAwait(false);
}

internal async override Task<IJSHandle> EvaluateExpressionHandleAsync(string script)
=> CreateHandleAsync(await EvaluateAsync(false, true, script).ConfigureAwait(false));

internal async override Task<IJSHandle> EvaluateFunctionHandleAsync(string script, params object[] args)
=> CreateHandleAsync(await EvaluateAsync(false, false, script).ConfigureAwait(false));
=> CreateHandleAsync(await EvaluateAsync(false, false, script, args).ConfigureAwait(false));

internal override async Task<T> EvaluateExpressionAsync<T>(string script)
=> DeserializeResult<T>((await EvaluateAsync(true, true, script).ConfigureAwait(false)).Result.Value);
Expand Down Expand Up @@ -236,14 +282,14 @@ private T DeserializeResult<T>(object result)
return (T)result;
}

private async Task<LocalValue> FormatArgumentAsync(object arg)
private async Task<ArgumentValue> FormatArgumentAsync(object arg)
{
if (arg is TaskCompletionSource<object> tcs)
{
arg = await tcs.Task.ConfigureAwait(false);
}

if (arg is BidiLazyArg lazyArg)
if (arg is LazyArg lazyArg)
{
arg = await lazyArg(this).ConfigureAwait(false);
}
Expand Down Expand Up @@ -289,16 +335,17 @@ private async Task<LocalValue> FormatArgumentAsync(object arg)
case float floatValue:
return LocalValue.Number(floatValue);
case IEnumerable enumerable:
var list = new List<LocalValue>();
var list = new List<ArgumentValue>();
foreach (var item in enumerable)
{
list.Add(await FormatArgumentAsync(item).ConfigureAwait(false));
}

return LocalValue.Array(list);
case IJSHandle objectHandle:
// TODO: Implement this
return null;
return LocalValue.Array(list.Select(el => el as LocalValue).ToList());
case BidiJSHandle objectHandle:
return objectHandle.RemoteValue.ToRemoteReference();
case BidiElementHandle elementHandle:
return elementHandle.Value.ToRemoteReference();

// TODO: Cover the rest of the cases
}
Expand Down
3 changes: 3 additions & 0 deletions lib/PuppeteerSharp/Bidi/Core/Realm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ public Task<EvaluateResult> CallFunctionAsync(
return Session.Driver.Script.CallFunctionAsync(parameters);
}

public Task DisownAsync(string[] handleIds)
=> Session.Driver.Script.DisownAsync(new DisownCommandParameters(Target, handleIds));

protected virtual void OnUpdated() => Updated?.Invoke(this, EventArgs.Empty);

private void OnDestroyed() => Destroyed?.Invoke(this, new ClosedEventArgs(_reason));
Expand Down
2 changes: 2 additions & 0 deletions lib/PuppeteerSharp/Bidi/Core/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ internal class Session(BiDiDriver driver, NewCommandResult info) : IDisposable

public Browser Browser { get; private set; }

internal ScriptInjector ScriptInjector { get; } = new();

public static async Task<Session> FromAsync(BiDiDriver driver, NewCommandParameters capabilities, ILoggerFactory loggerFactory)
{
var result = await driver.Session.NewSessionAsync(capabilities).ConfigureAwait(false);
Expand Down
14 changes: 0 additions & 14 deletions lib/PuppeteerSharp/Cdp/CdpElementHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,20 +162,6 @@ await handle.Client.SendAsync(
return handle;
});

/// <inheritdoc />
public override async ValueTask DisposeAsync()
{
if (Disposed)
{
return;
}

Disposed = true;

await Handle.DisposeAsync().ConfigureAwait(false);
GC.SuppressFinalize(this);
}

/// <inheritdoc />
public override string ToString() => Handle.ToString();

Expand Down
Loading
Loading