diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index b902417b4..3c512398f 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -87,6 +87,9 @@ jobs: - name: Build working-directory: lib run: dotnet build PuppeteerSharp.sln + - name: Disable AppArmor + if: matrix.os == 'ubuntu-latest' + run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns - name: Test (Linux) if: matrix.os == 'ubuntu-latest' env: diff --git a/README.md b/README.md index ce28f974d..9d918e8d6 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ await page.GoToAsync("http://www.google.com"); // In case of fonts being loaded await page.EvaluateExpressionHandleAsync("document.fonts.ready"); // Wait for fonts to be loaded. Omitting this might result in no text rendered in pdf. await page.PdfAsync(outputFile); ``` -snippet source | anchor +snippet source | anchor ### Inject HTML diff --git a/docfx_project/docs/IssuesGeneratingPdfFiles.md b/docfx_project/docs/IssuesGeneratingPdfFiles.md index f746db6ef..502f57630 100644 --- a/docfx_project/docs/IssuesGeneratingPdfFiles.md +++ b/docfx_project/docs/IssuesGeneratingPdfFiles.md @@ -27,6 +27,8 @@ if (!installedBrowser.PermissionsFixed) If PuppeteerSharp did not manage to apply the sandbox permissions, you can manually fix this by running the `setup.exe` file that was downloaded with the browser: +**IMPORTANT**: You need to run this as administrator. + ```bash cd .\setup.exe --configure-browser-in-directory="" diff --git a/lib/PuppeteerSharp.Tests/EmulationTests/PageEmulateNetworkConditionsTests.cs b/lib/PuppeteerSharp.Tests/EmulationTests/PageEmulateNetworkConditionsTests.cs index 00806e5b3..6cf7bb1ef 100644 --- a/lib/PuppeteerSharp.Tests/EmulationTests/PageEmulateNetworkConditionsTests.cs +++ b/lib/PuppeteerSharp.Tests/EmulationTests/PageEmulateNetworkConditionsTests.cs @@ -13,10 +13,16 @@ public PageEmulateNetworkConditionsTests() : base() [Test, Retry(2), PuppeteerTest("emulation.spec", "Emulation Page.emulateNetworkConditions", "should change navigator.connection.effectiveType")] public async Task ShouldChangeNavigatorConnectionEffectiveType() { - var slow3G = Puppeteer.NetworkConditions[NetworkConditions.Slow3G]; + var fast4G = Puppeteer.NetworkConditions[NetworkConditions.Fast4G]; + var slow4G = Puppeteer.NetworkConditions[NetworkConditions.Slow4G]; var fast3G = Puppeteer.NetworkConditions[NetworkConditions.Fast3G]; + var slow3G = Puppeteer.NetworkConditions[NetworkConditions.Slow3G]; Assert.That(await Page.EvaluateExpressionAsync("window.navigator.connection.effectiveType").ConfigureAwait(false), Is.EqualTo("4g")); + await Page.EmulateNetworkConditionsAsync(fast4G); + Assert.That(await Page.EvaluateExpressionAsync("window.navigator.connection.effectiveType").ConfigureAwait(false), Is.EqualTo("4g")); + await Page.EmulateNetworkConditionsAsync(slow4G); + Assert.That(await Page.EvaluateExpressionAsync("window.navigator.connection.effectiveType").ConfigureAwait(false), Is.EqualTo("3g")); await Page.EmulateNetworkConditionsAsync(fast3G); Assert.That(await Page.EvaluateExpressionAsync("window.navigator.connection.effectiveType").ConfigureAwait(false), Is.EqualTo("3g")); await Page.EmulateNetworkConditionsAsync(slow3G); diff --git a/lib/PuppeteerSharp.Tests/PageTests/PdfTests.cs b/lib/PuppeteerSharp.Tests/PageTests/PdfTests.cs index 3cc0b7a92..ea4c2e917 100644 --- a/lib/PuppeteerSharp.Tests/PageTests/PdfTests.cs +++ b/lib/PuppeteerSharp.Tests/PageTests/PdfTests.cs @@ -3,6 +3,7 @@ using System.Text.Json; using System.Threading.Tasks; using NUnit.Framework; +using PuppeteerSharp.Cdp; using PuppeteerSharp.Media; using PuppeteerSharp.Nunit; @@ -127,6 +128,8 @@ public void PdfOptionsShouldBeSerializable() { var pdfOptions = new PdfOptions { + Width = 100, + Height = 100, Format = PaperFormat.A4, DisplayHeaderFooter = true, MarginOptions = new MarginOptions @@ -145,25 +148,13 @@ public void PdfOptionsShouldBeSerializable() } [Test] - public void PdfOptionsShouldWorkWithMarginWithNoUnits() + public void ConvertPrintParameterToInchesTests() { - var pdfOptions = new PdfOptions - { - Format = PaperFormat.A4, - DisplayHeaderFooter = true, - MarginOptions = new MarginOptions - { - Top = "0", - Right = "0", - Bottom = "0", - Left = "0" - }, - FooterTemplate = "
- -
" - }; - - var serialized = JsonSerializer.Serialize(pdfOptions); - var newPdfOptions = JsonSerializer.Deserialize(serialized); - Assert.That(newPdfOptions, Is.EqualTo(pdfOptions)); + Assert.That(CdpPage.ConvertPrintParameterToInches("10"), Is.EqualTo(10m / 96)); + Assert.That(CdpPage.ConvertPrintParameterToInches("10px"), Is.EqualTo(10m / 96)); + Assert.That(CdpPage.ConvertPrintParameterToInches("0"), Is.EqualTo(0)); + Assert.That(CdpPage.ConvertPrintParameterToInches("0px"), Is.EqualTo(0)); + Assert.That(CdpPage.ConvertPrintParameterToInches("10in"), Is.EqualTo(10)); } } } diff --git a/lib/PuppeteerSharp.Tests/WorkerTests/PageWorkerTests.cs b/lib/PuppeteerSharp.Tests/WorkerTests/PageWorkerTests.cs index 144b4b09c..22ce8dafc 100644 --- a/lib/PuppeteerSharp.Tests/WorkerTests/PageWorkerTests.cs +++ b/lib/PuppeteerSharp.Tests/WorkerTests/PageWorkerTests.cs @@ -25,7 +25,7 @@ await Task.WhenAll( Page.GoToAsync(TestConstants.ServerUrl + "/worker/worker.html")); var worker = Page.Workers[0]; Assert.That(worker.Url, Does.Contain("worker.js")); - Assert.That(await worker.EvaluateExpressionAsync("self.workerFunction()"), Is.EqualTo("worker function result")); + Assert.That(await worker.EvaluateExpressionAsync("globalThis.workerFunction()"), Is.EqualTo("worker function result")); await Page.GoToAsync(TestConstants.EmptyPage); await workerDestroyedTcs.Task.WithTimeout(); diff --git a/lib/PuppeteerSharp/BrowserData/Chrome.cs b/lib/PuppeteerSharp/BrowserData/Chrome.cs index 7522fa3d8..8b3fe7ef6 100644 --- a/lib/PuppeteerSharp/BrowserData/Chrome.cs +++ b/lib/PuppeteerSharp/BrowserData/Chrome.cs @@ -13,7 +13,7 @@ public static class Chrome /// /// Default chrome build. /// - public static string DefaultBuildId => "128.0.6613.119"; + public static string DefaultBuildId => "129.0.6668.100"; internal static async Task ResolveBuildIdAsync(ChromeReleaseChannel channel) => (await GetLastKnownGoodReleaseForChannel(channel).ConfigureAwait(false)).Version; diff --git a/lib/PuppeteerSharp/Cdp/CdpPage.cs b/lib/PuppeteerSharp/Cdp/CdpPage.cs index 94db19b92..792a9f305 100644 --- a/lib/PuppeteerSharp/Cdp/CdpPage.cs +++ b/lib/PuppeteerSharp/Cdp/CdpPage.cs @@ -731,6 +731,48 @@ internal static async Task CreateAsync( } } + internal static decimal ConvertPrintParameterToInches(object parameter) + { + if (parameter == null) + { + return 0; + } + + decimal pixels; + if (parameter is decimal or int) + { + pixels = Convert.ToDecimal(parameter, CultureInfo.CurrentCulture); + } + else + { + var text = parameter.ToString(); + var unit = text.Length > 2 ? text.Substring(text.Length - 2).ToLower(CultureInfo.CurrentCulture) : string.Empty; + string valueText; + if (GetPixels(unit) is { }) + { + valueText = text.Substring(0, text.Length - 2); + } + else + { + // In case of unknown unit try to parse the whole parameter as number of pixels. + // This is consistent with phantom's paperSize behavior. + unit = "px"; + valueText = text; + } + + if (decimal.TryParse(valueText, NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out var number)) + { + pixels = number * GetPixels(unit).Value; + } + else + { + throw new ArgumentException($"Failed to parse parameter value: '{text}'", nameof(parameter)); + } + } + + return pixels / 96; + } + /// protected override async Task ExposeFunctionAsync(string name, Delegate puppeteerFunction) { @@ -1214,48 +1256,6 @@ await PrimaryTargetClient.SendAsync( } } - private decimal ConvertPrintParameterToInches(object parameter) - { - if (parameter == null) - { - return 0; - } - - decimal pixels; - if (parameter is decimal or int) - { - pixels = Convert.ToDecimal(parameter, CultureInfo.CurrentCulture); - } - else - { - var text = parameter.ToString(); - var unit = text.Length > 2 ? text.Substring(text.Length - 2).ToLower(CultureInfo.CurrentCulture) : string.Empty; - string valueText; - if (GetPixels(unit) is { }) - { - valueText = text.Substring(0, text.Length - 2); - } - else - { - // In case of unknown unit try to parse the whole parameter as number of pixels. - // This is consistent with phantom's paperSize behavior. - unit = "px"; - valueText = text; - } - - if (decimal.TryParse(valueText, NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out var number)) - { - pixels = number * GetPixels(unit).Value; - } - else - { - throw new ArgumentException($"Failed to parse parameter value: '{text}'", nameof(parameter)); - } - } - - return pixels / 96; - } - private Clip GetIntersectionRect(Clip clip, BoundingBox viewport) { var x = Math.Max(clip.X, viewport.X); diff --git a/lib/PuppeteerSharp/Helpers/Json/PrimitiveTypeConverter.cs b/lib/PuppeteerSharp/Helpers/Json/PrimitiveTypeConverter.cs new file mode 100644 index 000000000..e16772c64 --- /dev/null +++ b/lib/PuppeteerSharp/Helpers/Json/PrimitiveTypeConverter.cs @@ -0,0 +1,65 @@ +#nullable enable + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace PuppeteerSharp.Helpers.Json +{ + /// + /// Support types (, and ) + /// used by and for serialization / deserialization. + /// For usecases like . + /// + internal sealed class PrimitiveTypeConverter : JsonConverter + { + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + else if (reader.TokenType == JsonTokenType.String) + { + return reader.GetString(); + } + else if (reader.TokenType == JsonTokenType.Number) + { + if (reader.TryGetInt32(out var i)) + { + return i; + } + else if (reader.TryGetDecimal(out var dec)) + { + return dec; + } + } + + return JsonSerializer.Deserialize(ref reader, typeToConvert, options); + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else if (value is string str) + { + writer.WriteStringValue(str); + } + else if (value is decimal dec) + { + writer.WriteNumberValue(dec); + } + else if (value is int i) + { + writer.WriteNumberValue(i); + } + else + { + JsonSerializer.Serialize(writer, value, options); + } + } + } +} diff --git a/lib/PuppeteerSharp/Launcher.cs b/lib/PuppeteerSharp/Launcher.cs index 940740067..06bf0fda1 100644 --- a/lib/PuppeteerSharp/Launcher.cs +++ b/lib/PuppeteerSharp/Launcher.cs @@ -51,14 +51,19 @@ public async Task LaunchAsync(LaunchOptions options) EnsureSingleLaunchOrConnect(); _browser = options.Browser; - var buildId = options.Browser switch + + var executable = options.ExecutablePath; + if (executable == null) { - SupportedBrowser.Firefox => await Firefox.GetDefaultBuildIdAsync().ConfigureAwait(false), - SupportedBrowser.Chrome or SupportedBrowser.ChromeHeadlessShell => Chrome.DefaultBuildId, - SupportedBrowser.Chromium => await Chromium.ResolveBuildIdAsync(BrowserFetcher.GetCurrentPlatform()).ConfigureAwait(false), - _ => throw new ArgumentException("Invalid browser"), - }; - var executable = options.ExecutablePath ?? GetExecutablePath(options, buildId); + var buildId = options.Browser switch + { + SupportedBrowser.Firefox => await Firefox.GetDefaultBuildIdAsync().ConfigureAwait(false), + SupportedBrowser.Chrome or SupportedBrowser.ChromeHeadlessShell => Chrome.DefaultBuildId, + SupportedBrowser.Chromium => await Chromium.ResolveBuildIdAsync(BrowserFetcher.GetCurrentPlatform()).ConfigureAwait(false), + _ => throw new ArgumentException("Invalid browser"), + }; + executable = GetExecutablePath(options, buildId); + } Process = options.Browser switch { diff --git a/lib/PuppeteerSharp/Media/MarginOptions.cs b/lib/PuppeteerSharp/Media/MarginOptions.cs index 64b326c50..056470a8a 100644 --- a/lib/PuppeteerSharp/Media/MarginOptions.cs +++ b/lib/PuppeteerSharp/Media/MarginOptions.cs @@ -1,12 +1,12 @@ -using System; -using System.Collections.Generic; +using System.Text.Json.Serialization; +using PuppeteerSharp.Helpers.Json; namespace PuppeteerSharp.Media { /// /// margin options used in . /// - public class MarginOptions : IEquatable + public record MarginOptions { /// /// Initializes a new instance of the class. @@ -18,61 +18,25 @@ public MarginOptions() /// /// Top margin, accepts values labeled with units. /// - public string Top { get; set; } + [JsonConverter(typeof(PrimitiveTypeConverter))] + public object Top { get; set; } /// /// Left margin, accepts values labeled with units. /// - public string Left { get; set; } + [JsonConverter(typeof(PrimitiveTypeConverter))] + public object Left { get; set; } /// /// Bottom margin, accepts values labeled with units. /// - public string Bottom { get; set; } + [JsonConverter(typeof(PrimitiveTypeConverter))] + public object Bottom { get; set; } /// /// Right margin, accepts values labeled with units. /// - public string Right { get; set; } - - /// Overriding == operator for . - /// the value to compare against . - /// the value to compare against . - /// true if the two instances are equal to the same value. - public static bool operator ==(MarginOptions left, MarginOptions right) - => EqualityComparer.Default.Equals(left, right); - - /// Overriding != operator for . - /// the value to compare against . - /// the value to compare against . - /// true if the two instances are not equal to the same value. - public static bool operator !=(MarginOptions left, MarginOptions right) => !(left == right); - - /// - public override bool Equals(object obj) - { - if (obj == null || GetType() != obj.GetType()) - { - return false; - } - - return Equals((MarginOptions)obj); - } - - /// - public bool Equals(MarginOptions options) - => options != null && - Top == options.Top && - Left == options.Left && - Bottom == options.Bottom && - Right == options.Right; - - /// - public override int GetHashCode() - => -481391125 - ^ EqualityComparer.Default.GetHashCode(Top) - ^ EqualityComparer.Default.GetHashCode(Left) - ^ EqualityComparer.Default.GetHashCode(Bottom) - ^ EqualityComparer.Default.GetHashCode(Right); + [JsonConverter(typeof(PrimitiveTypeConverter))] + public object Right { get; set; } } } diff --git a/lib/PuppeteerSharp/Media/PaperFormat.cs b/lib/PuppeteerSharp/Media/PaperFormat.cs index bd798300a..42c642132 100644 --- a/lib/PuppeteerSharp/Media/PaperFormat.cs +++ b/lib/PuppeteerSharp/Media/PaperFormat.cs @@ -1,13 +1,10 @@ -using System; -using System.Collections.Generic; - namespace PuppeteerSharp.Media { /// /// Paper format. /// /// - public class PaperFormat : IEquatable + public record PaperFormat { /// /// Initializes a new instance of the class. @@ -87,41 +84,5 @@ public PaperFormat(decimal width, decimal height) /// /// The Height. public decimal Height { get; set; } - - /// Overriding == operator for . - /// the value to compare against . - /// the value to compare against . - /// true if the two instances are equal to the same value. - public static bool operator ==(PaperFormat left, PaperFormat right) - => EqualityComparer.Default.Equals(left, right); - - /// Overriding != operator for . - /// the value to compare against . - /// the value to compare against . - /// true if the two instances are not equal to the same value. - public static bool operator !=(PaperFormat left, PaperFormat right) => !(left == right); - - /// - public override bool Equals(object obj) - { - if (obj == null || GetType() != obj.GetType()) - { - return false; - } - - return Equals((PaperFormat)obj); - } - - /// - public bool Equals(PaperFormat format) - => format != null && - Width == format.Width && - Height == format.Height; - - /// - public override int GetHashCode() - => 859600377 - ^ Width.GetHashCode() - ^ Height.GetHashCode(); } } diff --git a/lib/PuppeteerSharp/NetworkConditions.cs b/lib/PuppeteerSharp/NetworkConditions.cs index db7af87a7..9a6a8e3c4 100644 --- a/lib/PuppeteerSharp/NetworkConditions.cs +++ b/lib/PuppeteerSharp/NetworkConditions.cs @@ -15,6 +15,16 @@ public class NetworkConditions /// public const string Fast3G = "Fast 3G"; + /// + /// Key to be used with . + /// + public const string Slow4G = "Slow 4G"; + + /// + /// Key to be used with . + /// + public const string Fast4G = "Fast 4G"; + /// /// Download speed (bytes/s), `-1` to disable. /// diff --git a/lib/PuppeteerSharp/PdfOptions.cs b/lib/PuppeteerSharp/PdfOptions.cs index f079f9c04..d76760706 100644 --- a/lib/PuppeteerSharp/PdfOptions.cs +++ b/lib/PuppeteerSharp/PdfOptions.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; +using PuppeteerSharp.Helpers.Json; using PuppeteerSharp.Media; namespace PuppeteerSharp @@ -67,11 +69,13 @@ public PdfOptions() /// /// Paper width, accepts values labeled with units. /// + [JsonConverter(typeof(PrimitiveTypeConverter))] public object Width { get; set; } /// /// Paper height, accepts values labeled with units. /// + [JsonConverter(typeof(PrimitiveTypeConverter))] public object Height { get; set; } /// diff --git a/lib/PuppeteerSharp/PredefinedNetworkConditions.cs b/lib/PuppeteerSharp/PredefinedNetworkConditions.cs index 19dbfafd4..77c44edae 100644 --- a/lib/PuppeteerSharp/PredefinedNetworkConditions.cs +++ b/lib/PuppeteerSharp/PredefinedNetworkConditions.cs @@ -13,16 +13,51 @@ public static class PredefinedNetworkConditions { [NetworkConditions.Slow3G] = new NetworkConditions { + // ~500Kbps down Download = ((500 * 1000) / 8) * 0.8, + + // ~500Kbps up Upload = ((500 * 1000) / 8) * 0.8, + + // 400ms RTT Latency = 400 * 5, }, [NetworkConditions.Fast3G] = new NetworkConditions { + // ~1.6 Mbps down + Download = ((1.6 * 1000 * 1000) / 8) * 0.9, + + // ~0.75 Mbps up + Upload = ((750 * 1000) / 8) * 0.9, + + // 150ms RTT + Latency = 150 * 3.75, + }, + + // alias to Fast 3G to align with Lighthouse (crbug.com/342406608) + // and DevTools (crbug.com/342406608), + [NetworkConditions.Slow4G] = new NetworkConditions + { + // ~1.6 Mbps down Download = ((1.6 * 1000 * 1000) / 8) * 0.9, + + // ~0.75 Mbps up Upload = ((750 * 1000) / 8) * 0.9, + + // 150ms RTT Latency = 150 * 3.75, }, + [NetworkConditions.Fast4G] = new NetworkConditions + { + // 9 Mbps down + Download = ((9 * 1000 * 1000) / 8) * 0.9, + + // 1.5 Mbps up + Upload = ((1.5 * 1000 * 1000) / 8) * 0.9, + + // 60ms RTT + Latency = 60 * 2.75, + }, }; private static readonly Lazy> _readOnlyConditions = diff --git a/lib/PuppeteerSharp/PuppeteerSharp.csproj b/lib/PuppeteerSharp/PuppeteerSharp.csproj index 1ce826f32..9f57ce6cc 100644 --- a/lib/PuppeteerSharp/PuppeteerSharp.csproj +++ b/lib/PuppeteerSharp/PuppeteerSharp.csproj @@ -12,10 +12,10 @@ Headless Browser .NET API PuppeteerSharp - 20.0.2 - 20.0.2 - 20.0.2 - 20.0.2 + 20.0.3 + 20.0.3 + 20.0.3 + 20.0.3 false false embedded @@ -46,7 +46,7 @@ - + pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy