tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_webext_apis.js (5347B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const { AddonManager } = ChromeUtils.importESModule(
      7  "resource://gre/modules/AddonManager.sys.mjs"
      8 );
      9 const { ExtensionTestUtils } = ChromeUtils.importESModule(
     10  "resource://testing-common/ExtensionXPCShellUtils.sys.mjs"
     11 );
     12 
     13 const DistinctDevToolsServer = getDistinctDevToolsServer();
     14 ExtensionTestUtils.init(this);
     15 
     16 add_setup(async () => {
     17  Services.prefs.setBoolPref("extensions.blocklist.enabled", false);
     18  await startupAddonsManager();
     19 });
     20 
     21 // Basic request wrapper that sends a request and resolves on the next packet.
     22 // Will only work for very basic scenarios, without events emitted on the server
     23 // etc...
     24 async function sendRequest(transport, request) {
     25  return new Promise(resolve => {
     26    transport.hooks = {
     27      onPacket: packet => {
     28        dump(`received packet: ${JSON.stringify(packet)}\n`);
     29        // Let's resolve only when we get a packet that is related to our
     30        // request. It is needed because some methods do not return the correct
     31        // response right away. This is the case of the `reload` method, which
     32        // receives a `addonListChanged` message first and then a `reload`
     33        // message.
     34        if (packet.from === request.to) {
     35          resolve(packet);
     36        }
     37      },
     38    };
     39    transport.send(request);
     40  });
     41 }
     42 
     43 // If this test case fails, please reach out to webext peers because
     44 // https://github.com/mozilla/web-ext relies on the APIs tested here.
     45 add_task(async function test_webext_run_apis() {
     46  DistinctDevToolsServer.init();
     47  DistinctDevToolsServer.registerAllActors();
     48 
     49  const transport = DistinctDevToolsServer.connectPipe();
     50 
     51  // After calling connectPipe, the root actor will be created on the server
     52  // and a packet will be emitted after a tick. Wait for the initial packet.
     53  await new Promise(resolve => {
     54    transport.hooks = { onPacket: resolve };
     55  });
     56 
     57  const getRootResponse = await sendRequest(transport, {
     58    to: "root",
     59    type: "getRoot",
     60  });
     61 
     62  ok(getRootResponse, "received a response after calling RootActor::getRoot");
     63  ok(getRootResponse.addonsActor, "getRoot returned an addonsActor id");
     64 
     65  // installTemporaryAddon
     66  const addonId = "test-addons-actor@mozilla.org";
     67  const addonPath = getFilePath("addons/web-extension", false, true);
     68  const promiseStarted = AddonTestUtils.promiseWebExtensionStartup(addonId);
     69  const { addon } = await sendRequest(transport, {
     70    to: getRootResponse.addonsActor,
     71    type: "installTemporaryAddon",
     72    addonPath,
     73    // The openDevTools parameter is not always passed by web-ext. This test
     74    // omits it, to make sure that the request without the flag is accepted.
     75    // openDevTools: false,
     76  });
     77  await promiseStarted;
     78 
     79  ok(addon, "addonsActor allows to install a temporary add-on");
     80  equal(addon.id, addonId, "temporary add-on is the expected one");
     81  equal(addon.actor, false, "temporary add-on does not have an actor");
     82 
     83  // listAddons
     84  let { addons } = await sendRequest(transport, {
     85    to: "root",
     86    type: "listAddons",
     87  });
     88  ok(Array.isArray(addons), "listAddons() returns a list of add-ons");
     89  equal(addons.length, 1, "expected an add-on installed");
     90 
     91  const installedAddon = addons[0];
     92  equal(installedAddon.id, addonId, "installed add-on is the expected one");
     93  ok(installedAddon.actor, "returned add-on has an actor");
     94 
     95  // reload
     96  const promiseReloaded = AddonTestUtils.promiseAddonEvent("onInstalled");
     97  const promiseRestarted = AddonTestUtils.promiseWebExtensionStartup(addonId);
     98  await sendRequest(transport, {
     99    to: installedAddon.actor,
    100    type: "reload",
    101  });
    102  await Promise.all([promiseReloaded, promiseRestarted]);
    103 
    104  // uninstallAddon
    105  const promiseUninstalled = new Promise(resolve => {
    106    const listener = {};
    107    listener.onUninstalled = uninstalledAddon => {
    108      if (uninstalledAddon.id == addonId) {
    109        AddonManager.removeAddonListener(listener);
    110        resolve();
    111      }
    112    };
    113    AddonManager.addAddonListener(listener);
    114  });
    115  await sendRequest(transport, {
    116    to: getRootResponse.addonsActor,
    117    type: "uninstallAddon",
    118    addonId,
    119  });
    120  await promiseUninstalled;
    121 
    122  ({ addons } = await sendRequest(transport, {
    123    to: "root",
    124    type: "listAddons",
    125  }));
    126  equal(addons.length, 0, "expected no add-on installed");
    127 
    128  // Attempt to uninstall an add-on that is (no longer) installed.
    129  let error = await sendRequest(transport, {
    130    to: getRootResponse.addonsActor,
    131    type: "uninstallAddon",
    132    addonId,
    133  });
    134  equal(
    135    error?.message,
    136    `Could not uninstall add-on "${addonId}"`,
    137    "expected error"
    138  );
    139 
    140  // Attempt to uninstall a non-temporarily loaded extension, which we do not
    141  // allow at the moment. We start by loading an extension, then we call the
    142  // `uninstallAddon`.
    143  const id = "not-a-temporary@extension";
    144  const extension = ExtensionTestUtils.loadExtension({
    145    manifest: {
    146      browser_specific_settings: { gecko: { id } },
    147    },
    148    useAddonManager: "permanent",
    149  });
    150  await extension.startup();
    151 
    152  error = await sendRequest(transport, {
    153    to: getRootResponse.addonsActor,
    154    type: "uninstallAddon",
    155    addonId: id,
    156  });
    157  equal(error?.message, `Could not uninstall add-on "${id}"`, "expected error");
    158 
    159  await extension.unload();
    160 
    161  transport.close();
    162 });