UseExistingPage

This commit is contained in:
Haiping Chen 2024-08-04 19:15:27 -05:00
parent f1e8ceefca
commit af833048fd
5 changed files with 63 additions and 56 deletions

View file

@ -4,7 +4,7 @@ namespace BotSharp.Abstraction.Browsing;
public interface IWebBrowser
{
void SetServiceProvider(IServiceProvider services);
void SetServiceProvider (IServiceProvider services);
Task<BrowserActionResult> LaunchBrowser(MessageInfo message, BrowserActionArgs args);
Task<BrowserActionResult> ScreenshotAsync(MessageInfo message, string path);
Task<BrowserActionResult> ScrollPage(MessageInfo message, PageActionArgs args);

View file

@ -10,7 +10,12 @@ public class PageActionArgs
public string? Direction { get; set; }
public string Url { get; set; } = null!;
/// <summary>
/// This value has to be set to true if you want to get the page XHR/ Fetch responses
/// </summary>
public bool OpenNewTab { get; set; } = false;
public bool UseExistingPage { get; set; } = false;
public bool WaitForNetworkIdle { get; set; } = true;
public float? Timeout { get; set; }

View file

@ -5,6 +5,8 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
public class PlaywrightInstance : IDisposable
{
IPlaywright _playwright;
IServiceProvider _services;
public IServiceProvider Services => _services;
Dictionary<string, IBrowserContext> _contexts = new Dictionary<string, IBrowserContext>();
Dictionary<string, List<IPage>> _pages = new Dictionary<string, List<IPage>>();
@ -18,6 +20,11 @@ public class PlaywrightInstance : IDisposable
/// </summary>
public Dictionary<string, List<IPage>> Pages => _pages;
public void SetServiceProvider(IServiceProvider services)
{
_services = services;
}
public IPage GetPage(string contextId, string? pattern = null)
{
if (string.IsNullOrEmpty(pattern))
@ -106,7 +113,7 @@ public class PlaywrightInstance : IDisposable
return _contexts[ctxId];
}
public async Task<IPage> NewPage(MessageInfo message, IServiceProvider services)
public async Task<IPage> NewPage(MessageInfo message)
{
var context = await GetContext(message.ContextId);
var page = await context.NewPageAsync();
@ -116,6 +123,43 @@ public class PlaywrightInstance : IDisposable
var js = @"Object.defineProperties(navigator, {webdriver:{get:()=>false}});";
await page.AddInitScriptAsync(js);
page.Response += async (sender, e) =>
{
if (e.Status != 204 &&
e.Headers.ContainsKey("content-type") &&
e.Headers["content-type"].Contains("application/json") &&
(e.Request.ResourceType == "fetch" || e.Request.ResourceType == "xhr"))
{
Serilog.Log.Information($"{e.Request.Method}: {e.Url}");
JsonElement? json = null;
try
{
if (e.Status == 200 && e.Ok)
{
json = await e.JsonAsync();
}
else
{
Serilog.Log.Warning($"Response status: {e.Status} {e.StatusText}, OK: {e.Ok}");
}
var webPageResponseHooks = _services.GetServices<IWebPageResponseHook>();
foreach (var hook in webPageResponseHooks)
{
hook.OnDataFetched(message, e.Url.ToLower(), e.Request?.PostData ?? string.Empty, JsonSerializer.Serialize(json));
}
}
catch (ObjectDisposedException ex)
{
Serilog.Log.Information(ex.Message);
}
catch (Exception ex)
{
Serilog.Log.Error(ex.ToString());
}
}
};
return page;
}

View file

@ -8,58 +8,11 @@ public partial class PlaywrightWebDriver
var context = await _instance.GetContext(message.ContextId);
try
{
// Check if the page is already open
/*if (!args.OpenNewTab && context.Pages.Count > 0)
{
foreach (var p in context.Pages)
{
if (p.Url == args.Url)
{
// Disable this due to performance issue, some page is too large
// result.Body = await p.ContentAsync();
result.IsSuccess = true;
// await p.BringToFrontAsync();
return result;
}
}
}*/
var page = args.UseExistingPage ?
_instance.GetPage(message.ContextId, pattern: args.Url) :
await _instance.NewPage(message);
var page = args.OpenNewTab ? await _instance.NewPage(message, _services) :
_instance.GetPage(message.ContextId, pattern: args.Url);
page.Response += async (sender, e) =>
{
if (e.Headers.ContainsKey("content-type") &&
e.Headers["content-type"].Contains("application/json") &&
(e.Request.ResourceType == "fetch" || e.Request.ResourceType == "xhr"))
{
Serilog.Log.Information($"{e.Request.Method}: {e.Url}");
JsonElement? json = null;
try
{
if (e.Status == 200 && e.Ok)
{
json = await e.JsonAsync();
}
else
{
Serilog.Log.Warning($"Response status: {e.Status} {e.StatusText}, OK: {e.Ok}");
}
var webPageResponseHooks = _services.GetServices<IWebPageResponseHook>();
foreach (var hook in webPageResponseHooks)
{
hook.OnDataFetched(message, e.Url.ToLower(), e.Request?.PostData ?? string.Empty, JsonSerializer.Serialize(json));
}
}
catch (Exception ex)
{
Serilog.Log.Error(ex.ToString());
}
}
};
if (!args.OpenNewTab && page != null && page.Url != "about:blank")
if (args.UseExistingPage && page != null && page.Url != "about:blank")
{
Serilog.Log.Information($"goto existing page: {args.Url}");
result.IsSuccess = true;
@ -68,6 +21,11 @@ public partial class PlaywrightWebDriver
Serilog.Log.Information($"goto page: {args.Url}");
if (args.UseExistingPage && args.OpenNewTab && page != null && page.Url == "about:blank")
{
page = await _instance.NewPage(message);
}
var response = await page.GotoAsync(args.Url, new PageGotoOptions
{
Timeout = args.Timeout

View file

@ -2,7 +2,7 @@ namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
public partial class PlaywrightWebDriver : IWebBrowser
{
private IServiceProvider _services;
private IServiceProvider _services => _instance.Services;
private readonly PlaywrightInstance _instance;
private readonly ILogger _logger;
public PlaywrightInstance Instance => _instance;
@ -12,9 +12,9 @@ public partial class PlaywrightWebDriver : IWebBrowser
public PlaywrightWebDriver(IServiceProvider services, PlaywrightInstance instance, ILogger<PlaywrightWebDriver> logger)
{
_services = services;
_instance = instance;
_logger = logger;
_instance.SetServiceProvider(services);
}
public void SetAgent(Agent agent)
@ -66,6 +66,6 @@ public partial class PlaywrightWebDriver : IWebBrowser
public void SetServiceProvider(IServiceProvider services)
{
_services = services;
_instance.SetServiceProvider(_services);
}
}