Skip to content

Commit 72d0541

Browse files
authored
Back up user.js as well (#2773)
* Back up user.js as well * bump patch * some fixes * self review * cr * mark as beta
1 parent 270c7a2 commit 72d0541

File tree

7 files changed

+122
-33
lines changed

7 files changed

+122
-33
lines changed

lib/PuppeteerSharp.Tests/LauncherTests/PuppeteerLaunchTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,5 +372,37 @@ public async Task ShouldSupportCustomTransport()
372372
Assert.That(customTransportCreated, Is.True);
373373
}
374374
}
375+
376+
[Test, Retry(2), PuppeteerTest("launcher.spec", "Launcher specs Puppeteer Puppeteer.launch", "userDataDir option restores preferences")]
377+
public async Task UserDataDirOptionRestoresPreferences()
378+
{
379+
using var userDataDir = new TempDirectory();
380+
var userDataDirInfo = new DirectoryInfo(userDataDir.Path);
381+
var prefsJSPath = Path.Combine(userDataDir.Path, "prefs.js");
382+
var userJSPath = Path.Combine(userDataDir.Path, "user.js");
383+
var prefsJSContent = """user_pref("browser.warnOnQuit", true);""";
384+
await File.WriteAllTextAsync(prefsJSPath, prefsJSContent);
385+
await File.WriteAllTextAsync(userJSPath, prefsJSContent);
386+
387+
var options = TestConstants.DefaultBrowserOptions();
388+
options.UserDataDir = userDataDir.Path;
389+
390+
await using var browser = await Puppeteer.LaunchAsync(options, TestConstants.LoggerFactory);
391+
await browser.NewPageAsync();
392+
393+
Assert.That(userDataDirInfo.GetFiles(), Is.Not.Empty);
394+
await browser.CloseAsync();
395+
await Assert.MultipleAsync(async () =>
396+
{
397+
Assert.That(userDataDirInfo.GetFiles(), Is.Not.Empty);
398+
399+
Assert.That(
400+
await File.ReadAllTextAsync(Path.Combine(userDataDir.Path, "prefs.js")),
401+
Is.EqualTo(prefsJSContent));
402+
Assert.That(
403+
await File.ReadAllTextAsync(Path.Combine(userDataDir.Path, "user.js")),
404+
Is.EqualTo(prefsJSContent));
405+
});
406+
}
375407
}
376408
}

lib/PuppeteerSharp/BrowserData/Firefox.cs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using System.Text.Json;
66
using System.Threading.Tasks;
7+
using Microsoft.Extensions.Options;
78
using PuppeteerSharp.Helpers;
89

910
namespace PuppeteerSharp.BrowserData
@@ -109,23 +110,33 @@ internal static string RelativeExecutablePath(Platform platform, string buildId)
109110

110111
internal static void CreateProfile(string tempUserDataDirectory, Dictionary<string, object> preferences)
111112
{
112-
// If the tempUserDataDirectory begins and ends with a quote, remove the quote
113-
if (tempUserDataDirectory.StartsWith("\"", StringComparison.OrdinalIgnoreCase) && tempUserDataDirectory.EndsWith("\"", StringComparison.OrdinalIgnoreCase))
114-
{
115-
tempUserDataDirectory = tempUserDataDirectory.Substring(1, tempUserDataDirectory.Length - 2);
116-
}
117-
113+
tempUserDataDirectory = tempUserDataDirectory.Unquote();
118114
var defaultPreferences = GetDefaultPreferences(preferences);
119115

120-
File.WriteAllText(
121-
Path.Combine(tempUserDataDirectory, "user.js"),
122-
string.Join(
123-
"\n",
124-
defaultPreferences.Select(i =>
125-
$"user_pref({JsonSerializer.Serialize(i.Key)}, {JsonSerializer.Serialize(i.Value)});")
126-
.ToArray()));
116+
SyncPreferences(defaultPreferences, tempUserDataDirectory);
117+
}
118+
119+
private static void SyncPreferences(Dictionary<string, object> defaultPreferences, string tempUserDataDirectory)
120+
{
121+
var prefsPath = Path.Combine(tempUserDataDirectory, "prefs.js");
122+
var userPath = Path.Combine(tempUserDataDirectory, "user.js");
123+
var lines = string.Join(
124+
"\n",
125+
defaultPreferences.Select(i => $"user_pref({JsonSerializer.Serialize(i.Key)}, {JsonSerializer.Serialize(i.Value)});").ToArray());
126+
127+
BackupFile(userPath);
128+
BackupFile(prefsPath);
129+
File.WriteAllText(userPath, lines);
130+
File.WriteAllText(prefsPath, string.Empty);
131+
}
127132

128-
File.WriteAllText(Path.Combine(tempUserDataDirectory, "prefs.js"), string.Empty);
133+
private static void BackupFile(string userPath)
134+
{
135+
if (File.Exists(userPath))
136+
{
137+
var backupPath = $"{userPath}.puppeteer";
138+
File.Copy(userPath, backupPath, true);
139+
}
129140
}
130141

131142
private static (FirefoxChannel Channel, string BuildId) ParseBuildId(string buildId)

lib/PuppeteerSharp/FirefoxLauncher.cs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using System.Linq;
45
using System.Runtime.InteropServices;
56
using System.Threading.Tasks;
@@ -20,6 +21,7 @@ public class FirefoxLauncher : LauncherBase
2021
];
2122

2223
private static readonly string[] _profileCommandLineArguments = ["-profile", "--profile"];
24+
private readonly string _userDataDir;
2325

2426
/// <summary>
2527
/// Initializes a new instance of the <see cref="FirefoxLauncher"/> class.
@@ -29,7 +31,7 @@ public class FirefoxLauncher : LauncherBase
2931
public FirefoxLauncher(string executable, LaunchOptions options)
3032
: base(executable, options)
3133
{
32-
(var firefoxArgs, TempUserDataDir) = PrepareFirefoxArgs(options);
34+
(var firefoxArgs, TempUserDataDir, _userDataDir) = PrepareFirefoxArgs(options);
3335

3436
Process.StartInfo.Arguments = string.Join(" ", firefoxArgs);
3537
}
@@ -79,7 +81,35 @@ internal static string[] GetDefaultArgs(LaunchOptions options)
7981
return firefoxArguments.ToArray();
8082
}
8183

82-
private static (List<string> FirefoxArgs, TempDirectory TempUserDataDirectory) PrepareFirefoxArgs(LaunchOptions options)
84+
internal override void OnExit()
85+
{
86+
// If TempUserDataDir is null it means that the user provided their own userDataDir
87+
if (TempUserDataDir is null)
88+
{
89+
var backupSuffix = ".puppeteer";
90+
string[] backupFiles = ["prefs.js", "user.js"];
91+
var basePath = _userDataDir.Unquote();
92+
foreach (var backupFile in backupFiles)
93+
{
94+
var backupPath = Path.Combine(basePath, backupFile + backupSuffix);
95+
var originalPath = Path.Combine(basePath, backupFile);
96+
if (File.Exists(backupPath))
97+
{
98+
// We don't have the overwrite parameter in netstandard
99+
if (File.Exists(originalPath))
100+
{
101+
File.Delete(originalPath);
102+
}
103+
104+
File.Move(backupPath, Path.Combine(basePath, backupFile));
105+
}
106+
}
107+
}
108+
109+
base.OnExit();
110+
}
111+
112+
private static (List<string> FirefoxArgs, TempDirectory TempUserDataDirectory, string UserDataDir) PrepareFirefoxArgs(LaunchOptions options)
83113
{
84114
var firefoxArguments = new List<string>();
85115

@@ -126,7 +156,7 @@ private static (List<string> FirefoxArgs, TempDirectory TempUserDataDirectory) P
126156

127157
Firefox.CreateProfile(userDataDir, GetPreferences(options.ExtraPrefsFirefox));
128158

129-
return (firefoxArguments, tempUserDataDirectory);
159+
return (firefoxArguments, tempUserDataDirectory, userDataDir);
130160
}
131161

132162
private static Dictionary<string, object> GetPreferences(Dictionary<string, object> optionsExtraPreferencesFirefox)

lib/PuppeteerSharp/Helpers/StringExtensions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ public static string Quote(this string value)
1919
return value;
2020
}
2121

22+
/// <summary>
23+
/// Unquotes the specified <see cref="string"/>.
24+
/// </summary>
25+
/// <param name="value">The string to unquote.</param>
26+
/// <returns>An unquoted string.</returns>
27+
public static string Unquote(this string value)
28+
=> IsQuoted(value) ? value.Substring(1, value.Length - 2) : value;
29+
2230
private static bool IsQuoted(this string value)
2331
{
2432
return value.StartsWith("\"", StringComparison.OrdinalIgnoreCase)

lib/PuppeteerSharp/LauncherBase.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,25 @@ await ExitCompletionSource.Task.WithTimeout(
146146
return true;
147147
}
148148

149+
/// <summary>
150+
/// Cleans up temporary user data directory.
151+
/// </summary>
152+
internal virtual void OnExit()
153+
{
154+
if (TempUserDataDir is { } tempUserDataDir)
155+
{
156+
tempUserDataDir
157+
.DeleteAsync()
158+
.ContinueWith(
159+
t => ExitCompletionSource.TrySetResult(true),
160+
TaskScheduler.Default);
161+
}
162+
else
163+
{
164+
ExitCompletionSource.TrySetResult(true);
165+
}
166+
}
167+
149168
/// <summary>
150169
/// Set Env Variables.
151170
/// </summary>

lib/PuppeteerSharp/PuppeteerSharp.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
<Description>Headless Browser .NET API</Description>
1313
<PackageId>PuppeteerSharp</PackageId>
1414
<PackageReleaseNotes></PackageReleaseNotes>
15-
<PackageVersion>20.0.0</PackageVersion>
16-
<ReleaseVersion>20.0.0</ReleaseVersion>
17-
<AssemblyVersion>20.0.0</AssemblyVersion>
18-
<FileVersion>20.0.0</FileVersion>
15+
<PackageVersion>20.0.1-beta1</PackageVersion>
16+
<ReleaseVersion>20.0.1</ReleaseVersion>
17+
<AssemblyVersion>20.0.1</AssemblyVersion>
18+
<FileVersion>20.0.1</FileVersion>
1919
<SynchReleaseVersion>false</SynchReleaseVersion>
2020
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
2121
<DebugType>embedded</DebugType>

lib/PuppeteerSharp/States/ExitedState.cs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,7 @@ public void EnterFrom(LauncherBase launcher, State fromState)
1919
}
2020
}
2121

22-
if (launcher.TempUserDataDir is { } tempUserDataDir)
23-
{
24-
tempUserDataDir
25-
.DeleteAsync()
26-
.ContinueWith(
27-
t => launcher.ExitCompletionSource.TrySetResult(true),
28-
TaskScheduler.Default);
29-
}
30-
else
31-
{
32-
launcher.ExitCompletionSource.TrySetResult(true);
33-
}
22+
launcher.OnExit();
3423
}
3524

3625
public override Task ExitAsync(LauncherBase p, TimeSpan timeout) => Task.CompletedTask;

0 commit comments

Comments
 (0)