wabbajack/Wabbajack.Lib/WebAutomation/CefSharpWrapper.cs

232 lines
7.5 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
2020-02-06 05:30:31 +00:00
using System.Net;
2020-02-11 00:30:38 +00:00
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CefSharp;
2020-02-06 05:30:31 +00:00
using Wabbajack.Common;
using Wabbajack.Common.Exceptions;
using Wabbajack.Lib.LibCefHelpers;
namespace Wabbajack.Lib.WebAutomation
{
public class CefSharpWrapper : IWebDriver
{
2020-04-10 01:29:53 +00:00
private readonly IWebBrowser _browser;
public Action<Uri>? DownloadHandler { get; set; }
public CefSharpWrapper(IWebBrowser browser)
{
_browser = browser;
2020-04-28 22:46:50 +00:00
_browser.DownloadHandler = new DownloadHandler(this);
_browser.LifeSpanHandler = new PopupBlocker(this);
}
public Task NavigateTo(Uri uri, CancellationToken? token = null)
{
var tcs = new TaskCompletionSource<bool>();
2020-04-10 01:29:53 +00:00
EventHandler<LoadingStateChangedEventArgs>? handler = null;
handler = (sender, e) =>
{
if (e.IsLoading) return;
_browser.LoadingStateChanged -= handler;
tcs.SetResult(true);
};
_browser.LoadingStateChanged += handler;
_browser.Load(uri.ToString());
token?.Register(() => tcs.TrySetCanceled());
return tcs.Task;
}
private readonly string[] KnownServerLoadStrings =
{
"<h1>Temporarily Unavailable</h1>",
"<center>Request Header Or Cookie Too Large</center>",
//"<html><head></head><body></body></html>"
2021-01-09 01:12:20 +00:00
"<span class=\"cf-error-code\">525</span>",
"<span class=\"cf-error-code\">522</span>",
};
private readonly (string, int)[] KnownErrorStrings =
{
("<h1>400 Bad Request</h1>", 400),
2021-01-09 01:12:20 +00:00
("We could not locate the item you are trying to view.", 404),
};
private static readonly Random RetryRandom = new Random();
public async Task<long> NavigateToAndDownload(Uri uri, AbsolutePath dest, bool quickMode = false, CancellationToken? token = null)
2020-12-29 23:15:47 +00:00
{
var oldCB = _browser.DownloadHandler;
var handler = new ReroutingDownloadHandler(this, dest, quickMode: quickMode, token);
2020-12-29 23:15:47 +00:00
_browser.DownloadHandler = handler;
try
{
int retryCount = 0;
RETRY:
await NavigateTo(uri, token);
var source = await _browser.GetSourceAsync();
foreach (var err in KnownServerLoadStrings)
{
if (!source.Contains(err))
continue;
if ((token?.IsCancellationRequested) == true)
{
throw new TimeoutException();
}
else
{
retryCount += 1;
var retry = RetryRandom.Next(retryCount * 5000, retryCount * 5000 * 2);
Utils.Log($"Got server load error from {uri} retying in {retry}ms [{err}]");
await Task.Delay(TimeSpan.FromMilliseconds(retry));
goto RETRY;
}
}
foreach (var (err, httpCode) in KnownErrorStrings)
{
if (source.Contains(err))
throw new HttpException(httpCode,$"Web driver failed: {err}");
}
Utils.Log($"Loaded page {uri} starting download...");
return await handler.TaskResult;
2020-12-29 23:15:47 +00:00
}
finally {
_browser.DownloadHandler = oldCB;
}
}
public async Task<string> EvaluateJavaScript(string text)
{
var result = await _browser.EvaluateScriptAsync(text);
if (!result.Success)
throw new Exception(result.Message);
return (string)result.Result;
}
public Task<Helpers.Cookie[]> GetCookies(string domainPrefix)
{
return Helpers.GetCookies(domainPrefix);
}
2020-02-06 05:30:31 +00:00
private const string CefStateName = "cef-state";
public async Task WaitForInitialized()
{
while (!_browser.IsBrowserInitialized)
await Task.Delay(100);
}
2020-02-11 00:30:38 +00:00
public string Location => _browser.Address;
}
2020-02-06 05:30:31 +00:00
2020-02-16 01:53:57 +00:00
public class PopupBlocker : ILifeSpanHandler
{
2020-04-10 01:29:53 +00:00
private readonly CefSharpWrapper _wrapper;
2020-02-16 01:53:57 +00:00
public PopupBlocker(CefSharpWrapper cefSharpWrapper)
{
_wrapper = cefSharpWrapper;
}
public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl,
string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures,
2020-04-10 01:29:53 +00:00
IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser? newBrowser)
2020-02-16 01:53:57 +00:00
{
// Block popups
2020-05-04 22:49:24 +00:00
newBrowser = null;
2020-02-16 01:53:57 +00:00
return true;
}
public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
}
public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
2020-04-28 22:46:50 +00:00
return false;
2020-02-16 01:53:57 +00:00
}
public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
}
}
2020-12-29 23:15:47 +00:00
public class ReroutingDownloadHandler : IDownloadHandler
{
private CefSharpWrapper _wrapper;
private AbsolutePath _path;
public TaskCompletionSource<long> _tcs = new TaskCompletionSource<long>();
private bool _quickMode;
private CancellationToken? _cancelationToken;
public Task<long> TaskResult => _tcs.Task;
2020-12-29 23:15:47 +00:00
public ReroutingDownloadHandler(CefSharpWrapper wrapper, AbsolutePath path, bool quickMode, CancellationToken? token)
2020-12-29 23:15:47 +00:00
{
_wrapper = wrapper;
_path = path;
_quickMode = quickMode;
_cancelationToken = token;
token?.Register(() => _tcs.TrySetCanceled());
2020-12-29 23:15:47 +00:00
}
public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
IBeforeDownloadCallback callback)
{
if (_quickMode) return;
2020-12-29 23:15:47 +00:00
callback.Continue(_path.ToString(), false);
}
public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
IDownloadItemCallback callback)
{
if (_quickMode)
{
callback.Cancel();
_tcs.TrySetResult(downloadItem.TotalBytes);
2020-12-29 23:15:47 +00:00
return;
}
if (downloadItem.IsComplete)
_tcs.TrySetResult(downloadItem.TotalBytes);
2020-12-29 23:15:47 +00:00
callback.Resume();
}
}
2020-02-06 05:30:31 +00:00
public class DownloadHandler : IDownloadHandler
{
private CefSharpWrapper _wrapper;
public DownloadHandler(CefSharpWrapper wrapper)
{
_wrapper = wrapper;
}
public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
IBeforeDownloadCallback callback)
{
2020-04-10 01:29:53 +00:00
_wrapper.DownloadHandler?.Invoke(new Uri(downloadItem.Url));
2020-02-06 05:30:31 +00:00
}
public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
IDownloadItemCallback callback)
{
callback.Cancel();
}
}
}