tor-browser

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

test_syncedtabs.js (9305B)


      1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
      2 * vim:set ts=2 sw=2 sts=2 et:
      3 */
      4 "use strict";
      5 
      6 const { Weave } = ChromeUtils.importESModule(
      7  "resource://services-sync/main.sys.mjs"
      8 );
      9 const { SyncedTabs } = ChromeUtils.importESModule(
     10  "resource://services-sync/SyncedTabs.sys.mjs"
     11 );
     12 
     13 Log.repository.getLogger("Sync.RemoteTabs").addAppender(new Log.DumpAppender());
     14 
     15 // A mock "Tabs" engine which the SyncedTabs module will use instead of the real
     16 // engine. We pass a constructor that Sync creates.
     17 function MockTabsEngine() {
     18  this.clients = {}; // We'll set this dynamically
     19  // Mock fxAccounts + recentDeviceList as if we hit the FxA server
     20  this.fxAccounts = {
     21    device: {
     22      recentDeviceList: [
     23        {
     24          id: 1,
     25          name: "updated desktop name",
     26          availableCommands: {
     27            "https://identity.mozilla.com/cmd/open-uri": "baz",
     28          },
     29        },
     30        {
     31          id: 2,
     32          name: "updated mobile name",
     33          availableCommands: {
     34            "https://identity.mozilla.com/cmd/open-uri": "boo",
     35          },
     36        },
     37      ],
     38    },
     39  };
     40 }
     41 
     42 MockTabsEngine.prototype = {
     43  name: "tabs",
     44  enabled: true,
     45 
     46  getAllClients() {
     47    return Object.values(this.clients);
     48  },
     49 
     50  getOpenURLs() {
     51    return new Set();
     52  },
     53 };
     54 
     55 let tabsEngine;
     56 
     57 // A clients engine that doesn't need to be a constructor.
     58 let MockClientsEngine = {
     59  clientSettings: null, // Set in `configureClients`.
     60 
     61  isMobile(guid) {
     62    if (!guid.endsWith("desktop") && !guid.endsWith("mobile")) {
     63      throw new Error(
     64        "this module expected guids to end with 'desktop' or 'mobile'"
     65      );
     66    }
     67    return guid.endsWith("mobile");
     68  },
     69  remoteClientExists(id) {
     70    return this.clientSettings[id] !== false;
     71  },
     72  getClientName(id) {
     73    if (this.clientSettings[id]) {
     74      return this.clientSettings[id];
     75    }
     76    let client = tabsEngine.clients[id];
     77    let fxaDevice = tabsEngine.fxAccounts.device.recentDeviceList.find(
     78      device => device.id === client.fxaDeviceId
     79    );
     80    return fxaDevice ? fxaDevice.name : client.clientName;
     81  },
     82 
     83  getClientFxaDeviceId(id) {
     84    if (this.clientSettings[id]) {
     85      return this.clientSettings[id];
     86    }
     87    return tabsEngine.clients[id].fxaDeviceId;
     88  },
     89 
     90  getClientType() {
     91    return "desktop";
     92  },
     93 };
     94 
     95 function configureClients(clients, clientSettings = {}) {
     96  // each client record is expected to have an id.
     97  for (let [guid, client] of Object.entries(clients)) {
     98    client.id = guid;
     99  }
    100  tabsEngine.clients = clients;
    101  // Apply clients collection overrides.
    102  MockClientsEngine.clientSettings = clientSettings;
    103  // Send an observer that pretends the engine just finished a sync.
    104  Services.obs.notifyObservers(null, "weave:engine:sync:finish", "tabs");
    105 }
    106 
    107 add_task(async function setup() {
    108  await Weave.Service.promiseInitialized;
    109  // Configure Sync with our mock tabs engine and force it to become initialized.
    110  await Weave.Service.engineManager.unregister("tabs");
    111  await Weave.Service.engineManager.register(MockTabsEngine);
    112  Weave.Service.clientsEngine = MockClientsEngine;
    113  tabsEngine = Weave.Service.engineManager.get("tabs");
    114 
    115  // Tell the Sync XPCOM service it is initialized.
    116  let weaveXPCService = Cc["@mozilla.org/weave/service;1"].getService(
    117    Ci.nsISupports
    118  ).wrappedJSObject;
    119  weaveXPCService.ready = true;
    120 });
    121 
    122 // The tests.
    123 add_task(async function test_noClients() {
    124  // no clients, can't be tabs.
    125  await configureClients({});
    126 
    127  let tabs = await SyncedTabs.getTabClients();
    128  equal(Object.keys(tabs).length, 0);
    129 });
    130 
    131 add_task(async function test_clientWithTabs() {
    132  await configureClients({
    133    guid_desktop: {
    134      clientName: "My Desktop",
    135      tabs: [
    136        {
    137          urlHistory: ["http://foo.com/"],
    138          icon: "http://foo.com/favicon",
    139          lastUsed: 1655745700, // Mon, 20 Jun 2022 17:21:40 GMT
    140        },
    141      ],
    142    },
    143    guid_mobile: {
    144      clientName: "My Phone",
    145      tabs: [],
    146    },
    147  });
    148 
    149  let clients = await SyncedTabs.getTabClients();
    150  equal(clients.length, 2);
    151  clients.sort((a, b) => {
    152    return a.name.localeCompare(b.name);
    153  });
    154  equal(clients[0].tabs.length, 1);
    155  equal(clients[0].tabs[0].url, "http://foo.com/");
    156  equal(clients[0].tabs[0].icon, "http://foo.com/favicon");
    157  equal(clients[0].tabs[0].lastUsed, 1655745700);
    158  // second client has no tabs.
    159  equal(clients[1].tabs.length, 0);
    160 });
    161 
    162 add_task(async function test_staleClientWithTabs() {
    163  await configureClients(
    164    {
    165      guid_desktop: {
    166        clientName: "My Desktop",
    167        tabs: [
    168          {
    169            urlHistory: ["http://foo.com/"],
    170            icon: "http://foo.com/favicon",
    171            lastUsed: 1655745750,
    172          },
    173        ],
    174      },
    175      guid_mobile: {
    176        clientName: "My Phone",
    177        tabs: [],
    178      },
    179      guid_stale_mobile: {
    180        clientName: "My Deleted Phone",
    181        tabs: [],
    182      },
    183      guid_stale_desktop: {
    184        clientName: "My Deleted Laptop",
    185        tabs: [
    186          {
    187            urlHistory: ["https://bar.com/"],
    188            icon: "https://bar.com/favicon",
    189            lastUsed: 1655745700,
    190          },
    191        ],
    192      },
    193      guid_stale_name_desktop: {
    194        clientName: "My Generic Device",
    195        tabs: [
    196          {
    197            urlHistory: ["https://example.edu/"],
    198            icon: "https://example.edu/favicon",
    199            lastUsed: 1655745800,
    200          },
    201        ],
    202      },
    203    },
    204    {
    205      guid_stale_mobile: false,
    206      guid_stale_desktop: false,
    207      // We should always use the device name from the clients collection, instead
    208      // of the possibly stale tabs collection.
    209      guid_stale_name_desktop: "My Laptop",
    210    }
    211  );
    212  let clients = await SyncedTabs.getTabClients();
    213  clients.sort((a, b) => {
    214    return a.name.localeCompare(b.name);
    215  });
    216  equal(clients.length, 3);
    217  equal(clients[0].name, "My Desktop");
    218  equal(clients[0].tabs.length, 1);
    219  equal(clients[0].tabs[0].url, "http://foo.com/");
    220  equal(clients[0].tabs[0].lastUsed, 1655745750);
    221  equal(clients[1].name, "My Laptop");
    222  equal(clients[1].tabs.length, 1);
    223  equal(clients[1].tabs[0].url, "https://example.edu/");
    224  equal(clients[1].tabs[0].lastUsed, 1655745800);
    225  equal(clients[2].name, "My Phone");
    226  equal(clients[2].tabs.length, 0);
    227 });
    228 
    229 add_task(async function test_clientWithTabsIconsDisabled() {
    230  Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteIcons", false);
    231  await configureClients({
    232    guid_desktop: {
    233      clientName: "My Desktop",
    234      tabs: [
    235        {
    236          urlHistory: ["http://foo.com/"],
    237          icon: "http://foo.com/favicon",
    238        },
    239      ],
    240    },
    241  });
    242 
    243  let clients = await SyncedTabs.getTabClients();
    244  equal(clients.length, 1);
    245  clients.sort((a, b) => {
    246    return a.name.localeCompare(b.name);
    247  });
    248  equal(clients[0].tabs.length, 1);
    249  equal(clients[0].tabs[0].url, "http://foo.com/");
    250  // Expect the default favicon due to the pref being false.
    251  equal(clients[0].tabs[0].icon, "page-icon:http://foo.com/");
    252  Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteIcons");
    253 });
    254 
    255 add_task(async function test_filter() {
    256  // Nothing matches.
    257  await configureClients({
    258    guid_desktop: {
    259      clientName: "My Desktop",
    260      tabs: [
    261        {
    262          urlHistory: ["http://foo.com/"],
    263          title: "A test page.",
    264        },
    265        {
    266          urlHistory: ["http://bar.com/"],
    267          title: "Another page.",
    268        },
    269      ],
    270    },
    271  });
    272 
    273  let clients = await SyncedTabs.getTabClients("foo");
    274  equal(clients.length, 1);
    275  equal(clients[0].tabs.length, 1);
    276  equal(clients[0].tabs[0].url, "http://foo.com/");
    277  // check it matches the title.
    278  clients = await SyncedTabs.getTabClients("test");
    279  equal(clients.length, 1);
    280  equal(clients[0].tabs.length, 1);
    281  equal(clients[0].tabs[0].url, "http://foo.com/");
    282 });
    283 
    284 add_task(async function test_duplicatesTabsAcrossClients() {
    285  await configureClients({
    286    guid_desktop: {
    287      clientName: "My Desktop",
    288      tabs: [
    289        {
    290          urlHistory: ["http://foo.com/"],
    291          title: "A test page.",
    292        },
    293      ],
    294    },
    295    guid_mobile: {
    296      clientName: "My Phone",
    297      tabs: [
    298        {
    299          urlHistory: ["http://foo.com/"],
    300          title: "A test page.",
    301        },
    302      ],
    303    },
    304  });
    305 
    306  let clients = await SyncedTabs.getTabClients();
    307  equal(clients.length, 2);
    308  equal(clients[0].tabs.length, 1);
    309  equal(clients[1].tabs.length, 1);
    310  equal(clients[0].tabs[0].url, "http://foo.com/");
    311  equal(clients[1].tabs[0].url, "http://foo.com/");
    312 });
    313 
    314 add_task(async function test_clientsTabUpdatedName() {
    315  // See the "fxAccounts" object in the MockEngine above for the device list
    316  await configureClients({
    317    guid_desktop: {
    318      clientName: "My Desktop",
    319      tabs: [
    320        {
    321          urlHistory: ["http://foo.com/"],
    322          icon: "http://foo.com/favicon",
    323        },
    324      ],
    325      fxaDeviceId: 1,
    326    },
    327    guid_mobile: {
    328      clientName: "My Phone",
    329      tabs: [
    330        {
    331          urlHistory: ["http://bar.com/"],
    332          icon: "http://bar.com/favicon",
    333        },
    334      ],
    335      fxaDeviceId: 2,
    336    },
    337  });
    338  let clients = await SyncedTabs.getTabClients();
    339  equal(clients.length, 2);
    340  equal(clients[0].name, "updated desktop name");
    341  equal(clients[1].name, "updated mobile name");
    342 });