Skip to content

Commit 1b20938

Browse files
committed
[dotnet] support installing unsigned Firefox add-ons including from directory
1 parent 106c89a commit 1b20938

3 files changed

Lines changed: 142 additions & 14 deletions

File tree

dotnet/src/webdriver/Firefox/FirefoxDriver.cs

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.Collections.ObjectModel;
2222
using System.Globalization;
2323
using System.IO;
24+
using System.IO.Compression;
2425
using OpenQA.Selenium.DevTools;
2526
using OpenQA.Selenium.Remote;
2627

@@ -256,11 +257,35 @@ public void SetContext(FirefoxCommandContext context)
256257
this.Execute(SetContextCommand, parameters);
257258
}
258259

260+
/// <summary>
261+
/// Installs a Firefox add-on from a directory.
262+
/// </summary>
263+
/// <param name="addOnDirectoryToInstall">Full path of the directory of the add-on to install.</param>
264+
/// <param name="temporary">Whether the add-on is temporary; required for unsigned add-ons.</param>
265+
public string InstallAddOnFromDirectory(string addOnDirectoryToInstall, bool temporary = false)
266+
{
267+
if (string.IsNullOrEmpty(addOnDirectoryToInstall))
268+
{
269+
throw new ArgumentNullException(nameof(addOnDirectoryToInstall), "Add-on file name must not be null or the empty string");
270+
}
271+
272+
if (!Directory.Exists(addOnDirectoryToInstall))
273+
{
274+
throw new ArgumentException("Directory " + addOnDirectoryToInstall + " does not exist", nameof(addOnDirectoryToInstall));
275+
}
276+
277+
string addOnFileToInstall = Path.Combine(Path.GetTempPath(), "addon" + new Random().Next() + ".zip");
278+
ZipFile.CreateFromDirectory(addOnDirectoryToInstall, addOnFileToInstall);
279+
280+
return this.InstallAddOn(addOnFileToInstall, temporary);
281+
}
282+
259283
/// <summary>
260284
/// Installs a Firefox add-on from a file, typically a .xpi file.
261285
/// </summary>
262286
/// <param name="addOnFileToInstall">Full path and file name of the add-on to install.</param>
263-
public void InstallAddOnFromFile(string addOnFileToInstall)
287+
/// <param name="temporary">Whether the add-on is temporary; required for unsigned add-ons.</param>
288+
public string InstallAddOnFromFile(string addOnFileToInstall, bool temporary = false)
264289
{
265290
if (string.IsNullOrEmpty(addOnFileToInstall))
266291
{
@@ -272,31 +297,31 @@ public void InstallAddOnFromFile(string addOnFileToInstall)
272297
throw new ArgumentException("File " + addOnFileToInstall + " does not exist", nameof(addOnFileToInstall));
273298
}
274299

275-
// Implementation note: There is a version of the install add-on
276-
// command that can be used with a file name directly, by passing
277-
// a "path" property in the parameters object of the command. If
278-
// delegating to the "use the base64-encoded blob" version causes
279-
// issues, we can change this method to use the file name directly
280-
// instead.
281300
byte[] addOnBytes = File.ReadAllBytes(addOnFileToInstall);
282-
string base64AddOn = Convert.ToBase64String(addOnBytes);
283-
this.InstallAddOn(base64AddOn);
301+
string base64EncodedAddOn = Convert.ToBase64String(addOnBytes);
302+
303+
return this.InstallAddOn(base64EncodedAddOn, temporary);
284304
}
285305

286306
/// <summary>
287307
/// Installs a Firefox add-on.
288308
/// </summary>
289309
/// <param name="base64EncodedAddOn">The base64-encoded string representation of the add-on binary.</param>
290-
public void InstallAddOn(string base64EncodedAddOn)
310+
/// <param name="temporary">Whether the add-on is temporary; required for unsigned add-ons.</param>
311+
public string InstallAddOn(string base64EncodedAddOn, bool temporary = false)
291312
{
292313
if (string.IsNullOrEmpty(base64EncodedAddOn))
293314
{
294315
throw new ArgumentNullException(nameof(base64EncodedAddOn), "Base64 encoded add-on must not be null or the empty string");
295316
}
296317

297-
Dictionary<string, object> parameters = new Dictionary<string, object>();
298-
parameters["addon"] = base64EncodedAddOn;
299-
this.Execute(InstallAddOnCommand, parameters);
318+
Dictionary<string, object> parameters = new Dictionary<string, object>
319+
{
320+
["addon"] = base64EncodedAddOn,
321+
["temporary"] = temporary
322+
};
323+
Response response = this.Execute(InstallAddOnCommand, parameters);
324+
return (string)response.Value;
300325
}
301326

302327
/// <summary>

dotnet/test/common/DriverTestFixture.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace OpenQA.Selenium
88
public abstract class DriverTestFixture
99
{
1010
public string alertsPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("alerts.html");
11+
public string blankPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("blank.html");
1112
public string macbethPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("macbeth.html");
1213
public string macbethTitle = "Macbeth: Entire Play";
1314

dotnet/test/firefox/FirefoxDriverTest.cs

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public void ShouldWaitUntilBrowserHasClosedProperly()
3939

4040
driver.Url = formsPage;
4141
IWebElement textarea = driver.FindElement(By.Id("withText"));
42-
string expectedText = "I like cheese" + System.Environment.NewLine
42+
string expectedText = "I like cheese" + System.Environment.NewLine
4343
+ System.Environment.NewLine + "It's really nice";
4444
textarea.Clear();
4545
textarea.SendKeys(expectedText);
@@ -252,6 +252,108 @@ public void ShouldAllowUserToSuccessfullyOverrideTheHomePage()
252252
}
253253
}
254254

255+
[Test]
256+
public void ShouldInstallAndUninstallXpiAddon()
257+
{
258+
FirefoxDriver firefoxDriver = driver as FirefoxDriver;
259+
260+
string extension = GetPath("webextensions-selenium-example.xpi");
261+
string id = firefoxDriver.InstallAddOnFromFile(extension);
262+
263+
driver.Url = blankPage;
264+
265+
IWebElement injected = firefoxDriver.FindElement(By.Id("webextensions-selenium-example"));
266+
Assert.That(injected.Text, Is.EqualTo("Content injected by webextensions-selenium-example"));
267+
268+
firefoxDriver.UninstallAddOn(id);
269+
270+
driver.Navigate().Refresh();
271+
Assert.That(driver.FindElements(By.Id("webextensions-selenium-example")).Count, Is.Zero);
272+
}
273+
274+
[Test]
275+
public void ShouldInstallAndUninstallUnSignedZipAddon()
276+
{
277+
FirefoxDriver firefoxDriver = driver as FirefoxDriver;
278+
279+
string extension = GetPath("webextensions-selenium-example-unsigned.zip");
280+
string id = firefoxDriver.InstallAddOnFromFile(extension, true);
281+
282+
driver.Url = blankPage;
283+
284+
IWebElement injected = firefoxDriver.FindElement(By.Id("webextensions-selenium-example"));
285+
Assert.That(injected.Text, Is.EqualTo("Content injected by webextensions-selenium-example"));
286+
287+
firefoxDriver.UninstallAddOn(id);
288+
289+
driver.Navigate().Refresh();
290+
Assert.That(driver.FindElements(By.Id("webextensions-selenium-example")).Count, Is.Zero);
291+
}
292+
293+
[Test]
294+
public void ShouldInstallAndUninstallSignedZipAddon()
295+
{
296+
FirefoxDriver firefoxDriver = driver as FirefoxDriver;
297+
298+
string extension = GetPath("webextensions-selenium-example.zip");
299+
string id = firefoxDriver.InstallAddOnFromFile(extension);
300+
301+
driver.Url = blankPage;
302+
303+
IWebElement injected = firefoxDriver.FindElement(By.Id("webextensions-selenium-example"));
304+
Assert.That(injected.Text, Is.EqualTo("Content injected by webextensions-selenium-example"));
305+
306+
firefoxDriver.UninstallAddOn(id);
307+
308+
driver.Navigate().Refresh();
309+
Assert.That(driver.FindElements(By.Id("webextensions-selenium-example")).Count, Is.Zero);
310+
}
311+
312+
[Test]
313+
public void ShouldInstallAndUninstallSignedDirAddon()
314+
{
315+
FirefoxDriver firefoxDriver = driver as FirefoxDriver;
316+
317+
string extension = GetPath("webextensions-selenium-example-signed");
318+
string id = firefoxDriver.InstallAddOnFromDirectory(extension);
319+
320+
driver.Url = blankPage;
321+
322+
IWebElement injected = firefoxDriver.FindElement(By.Id("webextensions-selenium-example"));
323+
Assert.That(injected.Text, Is.EqualTo("Content injected by webextensions-selenium-example"));
324+
325+
firefoxDriver.UninstallAddOn(id);
326+
327+
driver.Navigate().Refresh();
328+
Assert.That(driver.FindElements(By.Id("webextensions-selenium-example")).Count, Is.Zero);
329+
}
330+
331+
[Test]
332+
public void ShouldInstallAndUninstallUnSignedDirAddon()
333+
{
334+
FirefoxDriver firefoxDriver = driver as FirefoxDriver;
335+
336+
string extension = GetPath("webextensions-selenium-example");
337+
string id = firefoxDriver.InstallAddOnFromDirectory(extension, true);
338+
339+
driver.Url = blankPage;
340+
341+
IWebElement injected = firefoxDriver.FindElement(By.Id("webextensions-selenium-example"));
342+
Assert.That(injected.Text, Is.EqualTo("Content injected by webextensions-selenium-example"));
343+
344+
firefoxDriver.UninstallAddOn(id);
345+
346+
driver.Navigate().Refresh();
347+
Assert.That(driver.FindElements(By.Id("webextensions-selenium-example")).Count, Is.Zero);
348+
}
349+
350+
private string GetPath(string name)
351+
{
352+
string sCurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
353+
string sFile = Path.Combine(sCurrentDirectory, "../../../../common/extensions/" + name);
354+
return Path.GetFullPath(sFile);
355+
}
356+
255357
private static bool PlatformHasNativeEvents()
256358
{
257359
return true;

0 commit comments

Comments
 (0)