tor-browser

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

test_tab_tracker.js (10709B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 ChromeUtils.importESModule("resource://services-sync/engines/tabs.sys.mjs");
      5 const { Service } = ChromeUtils.importESModule(
      6  "resource://services-sync/service.sys.mjs"
      7 );
      8 
      9 const { SyncScheduler } = ChromeUtils.importESModule(
     10  "resource://services-sync/policies.sys.mjs"
     11 );
     12 
     13 var scheduler = new SyncScheduler(Service);
     14 let clientsEngine;
     15 
     16 add_task(async function setup() {
     17  await Service.promiseInitialized;
     18  clientsEngine = Service.clientsEngine;
     19 
     20  scheduler.setDefaults();
     21 });
     22 
     23 function fakeSvcWinMediator() {
     24  // actions on windows are captured in logs
     25  let logs = [];
     26  delete Services.wm;
     27 
     28  function getNext() {
     29    let elt = { addTopics: [], remTopics: [], numAPL: 0, numRPL: 0 };
     30    logs.push(elt);
     31    return {
     32      addEventListener(topic) {
     33        elt.addTopics.push(topic);
     34      },
     35      removeEventListener(topic) {
     36        elt.remTopics.push(topic);
     37      },
     38      gBrowser: {
     39        addProgressListener() {
     40          elt.numAPL++;
     41        },
     42        removeProgressListener() {
     43          elt.numRPL++;
     44        },
     45      },
     46    };
     47  }
     48 
     49  Services.wm = {
     50    getEnumerator() {
     51      return [getNext(), getNext()];
     52    },
     53  };
     54  return logs;
     55 }
     56 
     57 function fakeGetTabState(tab) {
     58  return tab;
     59 }
     60 
     61 function clearQuickWriteTimer(tracker) {
     62  if (tracker.tabsQuickWriteTimer) {
     63    tracker.tabsQuickWriteTimer.clear();
     64  }
     65 }
     66 
     67 add_task(async function run_test() {
     68  let engine = Service.engineManager.get("tabs");
     69  await engine.initialize();
     70  _("We assume that tabs have changed at startup.");
     71  let tracker = engine._tracker;
     72  tracker.getTabState = fakeGetTabState;
     73 
     74  Assert.ok(tracker.modified);
     75  Assert.ok(
     76    Utils.deepEquals(Object.keys(await engine.getChangedIDs()), [
     77      clientsEngine.localID,
     78    ])
     79  );
     80 
     81  let logs;
     82 
     83  _("Test listeners are registered on windows");
     84  logs = fakeSvcWinMediator();
     85  tracker.start();
     86  Assert.equal(logs.length, 2);
     87  for (let log of logs) {
     88    Assert.equal(log.addTopics.length, 3);
     89    Assert.ok(log.addTopics.includes("TabOpen"));
     90    Assert.ok(log.addTopics.includes("TabClose"));
     91    Assert.ok(log.addTopics.includes("unload"));
     92    Assert.equal(log.remTopics.length, 0);
     93    Assert.equal(log.numAPL, 1, "Added 1 progress listener");
     94    Assert.equal(log.numRPL, 0, "Didn't remove a progress listener");
     95  }
     96 
     97  _("Test listeners are unregistered on windows");
     98  logs = fakeSvcWinMediator();
     99  await tracker.stop();
    100  Assert.equal(logs.length, 2);
    101  for (let log of logs) {
    102    Assert.equal(log.addTopics.length, 0);
    103    Assert.equal(log.remTopics.length, 3);
    104    Assert.ok(log.remTopics.includes("TabOpen"));
    105    Assert.ok(log.remTopics.includes("TabClose"));
    106    Assert.ok(log.remTopics.includes("unload"));
    107    Assert.equal(log.numAPL, 0, "Didn't add a progress listener");
    108    Assert.equal(log.numRPL, 1, "Removed 1 progress listener");
    109  }
    110 
    111  _("Test tab listener");
    112  for (let evttype of ["TabOpen", "TabClose"]) {
    113    // Pretend we just synced.
    114    await tracker.clearChangedIDs();
    115    Assert.ok(!tracker.modified);
    116 
    117    // Send a fake tab event
    118    tracker.onTab({
    119      type: evttype,
    120      originalTarget: evttype,
    121      target: { entries: [], currentURI: "about:config" },
    122    });
    123    Assert.ok(tracker.modified);
    124    Assert.ok(
    125      Utils.deepEquals(Object.keys(await engine.getChangedIDs()), [
    126        clientsEngine.localID,
    127      ])
    128    );
    129  }
    130 
    131  // Pretend we just synced.
    132  await tracker.clearChangedIDs();
    133  Assert.ok(!tracker.modified);
    134 
    135  tracker.onTab({
    136    type: "TabOpen",
    137    originalTarget: "TabOpen",
    138    target: { entries: [], currentURI: "about:config" },
    139  });
    140  Assert.ok(
    141    Utils.deepEquals(Object.keys(await engine.getChangedIDs()), [
    142      clientsEngine.localID,
    143    ])
    144  );
    145 
    146  // Pretend we just synced and saw some progress listeners.
    147  await tracker.clearChangedIDs();
    148  Assert.ok(!tracker.modified);
    149  tracker.onLocationChange({ isTopLevel: false }, undefined, undefined, 0);
    150  Assert.ok(!tracker.modified, "non-toplevel request didn't flag as modified");
    151 
    152  tracker.onLocationChange(
    153    { isTopLevel: true },
    154    undefined,
    155    Services.io.newURI("https://www.mozilla.org"),
    156    Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT
    157  );
    158  Assert.ok(
    159    tracker.modified,
    160    "location change within the same document request did flag as modified"
    161  );
    162 
    163  tracker.onLocationChange(
    164    { isTopLevel: true },
    165    undefined,
    166    Services.io.newURI("https://www.mozilla.org")
    167  );
    168  Assert.ok(
    169    tracker.modified,
    170    "location change for a new top-level document flagged as modified"
    171  );
    172  Assert.ok(
    173    Utils.deepEquals(Object.keys(await engine.getChangedIDs()), [
    174      clientsEngine.localID,
    175    ])
    176  );
    177 });
    178 
    179 add_task(async function run_sync_on_tab_change_test() {
    180  let testPrefDelay = 20000;
    181 
    182  // This is the pref that determines sync delay after tab change
    183  Svc.PrefBranch.setIntPref(
    184    "syncedTabs.syncDelayAfterTabChange",
    185    testPrefDelay
    186  );
    187  // We should only be syncing on tab change if
    188  // the user has > 1 client
    189  Svc.PrefBranch.setIntPref("clients.devices.desktop", 1);
    190  Svc.PrefBranch.setIntPref("clients.devices.mobile", 1);
    191  scheduler.updateClientMode();
    192  Assert.equal(scheduler.numClients, 2);
    193 
    194  let engine = Service.engineManager.get("tabs");
    195 
    196  _("We assume that tabs have changed at startup.");
    197  let tracker = engine._tracker;
    198  tracker.getTabState = fakeGetTabState;
    199 
    200  Assert.ok(tracker.modified);
    201  Assert.ok(
    202    Utils.deepEquals(Object.keys(await engine.getChangedIDs()), [
    203      clientsEngine.localID,
    204    ])
    205  );
    206 
    207  _("Test sync is scheduled after a tab change");
    208  for (let evttype of ["TabOpen", "TabClose"]) {
    209    // Pretend we just synced
    210    await tracker.clearChangedIDs();
    211    clearQuickWriteTimer(tracker);
    212 
    213    // Send a fake tab event
    214    tracker.onTab({
    215      type: evttype,
    216      originalTarget: evttype,
    217      target: { entries: [], currentURI: "about:config" },
    218    });
    219    // Ensure the tracker fired
    220    Assert.ok(tracker.modified);
    221    // We should be more delayed at or more than what the pref is set at
    222    let nextSchedule = tracker.tabsQuickWriteTimer.delay;
    223    Assert.greaterOrEqual(nextSchedule, testPrefDelay);
    224  }
    225 
    226  _("Test sync is NOT scheduled after an unsupported tab open");
    227  for (let evttype of ["TabOpen"]) {
    228    // Send a fake tab event
    229    tracker.onTab({
    230      type: evttype,
    231      originalTarget: evttype,
    232      target: { entries: ["about:newtab"], currentURI: null },
    233    });
    234    // Ensure the tracker fired
    235    Assert.ok(tracker.modified);
    236    // We should be scheduling <= pref value
    237    Assert.lessOrEqual(scheduler.nextSync - Date.now(), testPrefDelay);
    238  }
    239 
    240  _("Test navigating within the same tab does NOT trigger a sync");
    241  // Pretend we just synced
    242  await tracker.clearChangedIDs();
    243  clearQuickWriteTimer(tracker);
    244 
    245  tracker.onLocationChange(
    246    { isTopLevel: true },
    247    undefined,
    248    Services.io.newURI("https://www.mozilla.org"),
    249    Ci.nsIWebProgressListener.LOCATION_CHANGE_RELOAD
    250  );
    251  Assert.ok(
    252    !tracker.modified,
    253    "location change for reloading doesn't trigger a sync"
    254  );
    255  Assert.ok(!tracker.tabsQuickWriteTimer, "reload does not trigger a sync");
    256 
    257  // Pretend we just synced
    258  await tracker.clearChangedIDs();
    259  clearQuickWriteTimer(tracker);
    260 
    261  _("Test navigating to an about page does trigger sync");
    262  tracker.onLocationChange(
    263    { isTopLevel: true },
    264    undefined,
    265    Services.io.newURI("about:config")
    266  );
    267  Assert.ok(tracker.modified, "about page does not trigger a tab modified");
    268  Assert.ok(
    269    tracker.tabsQuickWriteTimer,
    270    "about schema should trigger a sync happening soon"
    271  );
    272 
    273  _("Test adjusting the filterScheme pref works");
    274  // Pretend we just synced
    275  await tracker.clearChangedIDs();
    276  clearQuickWriteTimer(tracker);
    277 
    278  Svc.PrefBranch.setStringPref(
    279    "engine.tabs.filteredSchemes",
    280    // Removing the about scheme for this test
    281    "resource|chrome|file|blob|moz-extension"
    282  );
    283  tracker.onLocationChange(
    284    { isTopLevel: true },
    285    undefined,
    286    Services.io.newURI("about:config")
    287  );
    288  Assert.ok(
    289    tracker.modified,
    290    "about page triggers a modified after we changed the pref"
    291  );
    292  Assert.ok(
    293    tracker.tabsQuickWriteTimer,
    294    "about page should schedule a quickWrite sync soon after we changed the pref"
    295  );
    296 
    297  _("Test no sync after tab change for accounts with <= 1 clients");
    298  // Pretend we just synced
    299  await tracker.clearChangedIDs();
    300  clearQuickWriteTimer(tracker);
    301  // Setting clients to only 1 so we don't sync after a tab change
    302  Svc.PrefBranch.setIntPref("clients.devices.desktop", 1);
    303  Svc.PrefBranch.setIntPref("clients.devices.mobile", 0);
    304  scheduler.updateClientMode();
    305  Assert.equal(scheduler.numClients, 1);
    306 
    307  tracker.onLocationChange(
    308    { isTopLevel: true },
    309    undefined,
    310    Services.io.newURI("https://www.mozilla.org")
    311  );
    312  Assert.ok(
    313    tracker.modified,
    314    "location change for a new top-level document flagged as modified"
    315  );
    316  Assert.ok(
    317    !tracker.tabsQuickWriteTimer,
    318    "We should NOT be syncing shortly because there is only one client"
    319  );
    320 
    321  _("Changing the pref adjusts the sync schedule");
    322  Svc.PrefBranch.setIntPref("syncedTabs.syncDelayAfterTabChange", 10000); // 10seconds
    323  let delayPref = Svc.PrefBranch.getIntPref(
    324    "syncedTabs.syncDelayAfterTabChange"
    325  );
    326  let evttype = "TabOpen";
    327  Assert.equal(delayPref, 10000); // ensure our pref is at 10s
    328  // Only have task continuity if we have more than 1 device
    329  Svc.PrefBranch.setIntPref("clients.devices.desktop", 1);
    330  Svc.PrefBranch.setIntPref("clients.devices.mobile", 1);
    331  scheduler.updateClientMode();
    332  Assert.equal(scheduler.numClients, 2);
    333  clearQuickWriteTimer(tracker);
    334 
    335  // Fire ontab event
    336  tracker.onTab({
    337    type: evttype,
    338    originalTarget: evttype,
    339    target: { entries: [], currentURI: "about:config" },
    340  });
    341 
    342  // Ensure the tracker fired
    343  Assert.ok(tracker.modified);
    344  // We should be scheduling <= preference value
    345  Assert.equal(tracker.tabsQuickWriteTimer.delay, delayPref);
    346 
    347  _("We should not have a sync scheduled if pref is at 0");
    348 
    349  Svc.PrefBranch.setIntPref("syncedTabs.syncDelayAfterTabChange", 0);
    350  // Pretend we just synced
    351  await tracker.clearChangedIDs();
    352  clearQuickWriteTimer(tracker);
    353 
    354  // Fire ontab event
    355  evttype = "TabOpen";
    356  tracker.onTab({
    357    type: evttype,
    358    originalTarget: evttype,
    359    target: { entries: [], currentURI: "about:config" },
    360  });
    361  // Ensure the tracker fired
    362  Assert.ok(tracker.modified);
    363 
    364  // We should NOT be scheduled for a sync soon
    365  Assert.ok(!tracker.tabsQuickWriteTimer);
    366 
    367  scheduler.setDefaults();
    368  for (const pref of Svc.PrefBranch.getChildList("")) {
    369    Svc.PrefBranch.clearUserPref(pref);
    370  }
    371 });