tor-browser

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

test_remote_tabs.js (17601B)


      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 
     10 // A mock "Tabs" engine which autocomplete will use instead of the real
     11 // engine. We pass a constructor that Sync creates.
     12 function MockTabsEngine() {
     13  this.clients = null; // We'll set this dynamically
     14 }
     15 
     16 MockTabsEngine.prototype = {
     17  name: "tabs",
     18 
     19  startTracking() {},
     20  getAllClients() {
     21    return this.clients;
     22  },
     23 };
     24 
     25 // A clients engine that doesn't need to be a constructor.
     26 let MockClientsEngine = {
     27  getClientType(guid) {
     28    Assert.ok(guid.endsWith("desktop") || guid.endsWith("mobile"));
     29    return guid.endsWith("mobile") ? "phone" : "desktop";
     30  },
     31  remoteClientExists(_id) {
     32    return true;
     33  },
     34  getClientName(id) {
     35    return id.endsWith("mobile") ? "My Phone" : "My Desktop";
     36  },
     37 };
     38 
     39 // Configure the singleton engine for a test.
     40 function configureEngine(clients) {
     41  // Configure the instance Sync created.
     42  let engine = Weave.Service.engineManager.get("tabs");
     43  engine.clients = clients;
     44  Weave.Service.clientsEngine = MockClientsEngine;
     45  // Send an observer that pretends the engine just finished a sync.
     46  Services.obs.notifyObservers(null, "weave:engine:sync:finish", "tabs");
     47 }
     48 
     49 testEngine_setup();
     50 
     51 add_setup(async function () {
     52  // Tell Sync about the mocks.
     53  Weave.Service.engineManager.register(MockTabsEngine);
     54 
     55  // Tell the Sync XPCOM service it is initialized.
     56  let weaveXPCService = Cc["@mozilla.org/weave/service;1"].getService(
     57    Ci.nsISupports
     58  ).wrappedJSObject;
     59  weaveXPCService.ready = true;
     60 
     61  registerCleanupFunction(async () => {
     62    Services.prefs.clearUserPref("services.sync.username");
     63    Services.prefs.clearUserPref("services.sync.registerEngines");
     64    Services.prefs.clearUserPref("browser.urlbar.suggest.searches");
     65    Services.prefs.clearUserPref("browser.urlbar.suggest.quickactions");
     66    await cleanupPlaces();
     67  });
     68 
     69  Services.prefs.setCharPref("services.sync.username", "someone@somewhere.com");
     70  Services.prefs.setCharPref("services.sync.registerEngines", "");
     71  // Avoid hitting the network.
     72  Services.prefs.setBoolPref("browser.urlbar.suggest.searches", false);
     73  Services.prefs.setBoolPref("browser.urlbar.suggest.quickactions", false);
     74 });
     75 
     76 add_task(async function test_minimal() {
     77  // The minimal client and tabs info we can get away with.
     78  configureEngine([
     79    {
     80      id: "desktop",
     81      tabs: [
     82        {
     83          urlHistory: ["http://example.com/"],
     84        },
     85      ],
     86    },
     87  ]);
     88 
     89  let query = "ex";
     90  let context = createContext(query, { isPrivate: false });
     91  await check_results({
     92    context,
     93    matches: [
     94      makeSearchResult(context, {
     95        engineName: SUGGESTIONS_ENGINE_NAME,
     96        heuristic: true,
     97      }),
     98      makeRemoteTabResult(context, {
     99        uri: "http://example.com/",
    100        device: "My Desktop",
    101      }),
    102    ],
    103  });
    104 });
    105 
    106 add_task(async function test_maximal() {
    107  // Every field that could possibly exist on a remote record.
    108  configureEngine([
    109    {
    110      id: "mobile",
    111      tabs: [
    112        {
    113          urlHistory: ["http://example.com/"],
    114          title: "An Example",
    115          icon: "http://favicon",
    116        },
    117      ],
    118    },
    119  ]);
    120 
    121  let query = "ex";
    122  let context = createContext(query, { isPrivate: false });
    123  await check_results({
    124    context,
    125    matches: [
    126      makeSearchResult(context, {
    127        engineName: SUGGESTIONS_ENGINE_NAME,
    128        heuristic: true,
    129      }),
    130      makeRemoteTabResult(context, {
    131        uri: "http://example.com/",
    132        device: "My Phone",
    133        title: "An Example",
    134        iconUri: "cached-favicon:http://favicon/",
    135      }),
    136    ],
    137  });
    138 });
    139 
    140 add_task(async function test_noShowIcons() {
    141  Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteIcons", false);
    142  configureEngine([
    143    {
    144      id: "mobile",
    145      tabs: [
    146        {
    147          urlHistory: ["http://example.com/"],
    148          title: "An Example",
    149          icon: "http://favicon",
    150        },
    151      ],
    152    },
    153  ]);
    154 
    155  let query = "ex";
    156  let context = createContext(query, { isPrivate: false });
    157  await check_results({
    158    context,
    159    matches: [
    160      makeSearchResult(context, {
    161        engineName: SUGGESTIONS_ENGINE_NAME,
    162        heuristic: true,
    163      }),
    164      makeRemoteTabResult(context, {
    165        uri: "http://example.com/",
    166        device: "My Phone",
    167        title: "An Example",
    168        // expecting the default favicon due to that pref.
    169        iconUri: "",
    170      }),
    171    ],
    172  });
    173  Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteIcons");
    174 });
    175 
    176 add_task(async function test_tabsDisabledInUrlbar() {
    177  Services.prefs.setBoolPref("browser.urlbar.suggest.remotetab", false);
    178  configureEngine([
    179    {
    180      id: "mobile",
    181      tabs: [
    182        {
    183          urlHistory: ["http://example.com/"],
    184          title: "An Example",
    185          icon: "http://favicon",
    186        },
    187      ],
    188    },
    189  ]);
    190 
    191  let context = createContext("ex", { isPrivate: false });
    192  await check_results({
    193    context,
    194    matches: [
    195      makeSearchResult(context, {
    196        engineName: SUGGESTIONS_ENGINE_NAME,
    197        heuristic: true,
    198      }),
    199    ],
    200  });
    201 
    202  Services.prefs.clearUserPref("browser.urlbar.suggest.remotetab");
    203 });
    204 
    205 add_task(async function test_matches_title() {
    206  // URL doesn't match search expression, should still match the title.
    207  configureEngine([
    208    {
    209      id: "mobile",
    210      tabs: [
    211        {
    212          urlHistory: ["http://foo.com/"],
    213          title: "An Example",
    214        },
    215      ],
    216    },
    217  ]);
    218 
    219  let query = "ex";
    220  let context = createContext(query, { isPrivate: false });
    221  await check_results({
    222    context,
    223    matches: [
    224      makeSearchResult(context, {
    225        engineName: SUGGESTIONS_ENGINE_NAME,
    226        heuristic: true,
    227      }),
    228      makeRemoteTabResult(context, {
    229        uri: "http://foo.com/",
    230        device: "My Phone",
    231        title: "An Example",
    232      }),
    233    ],
    234  });
    235 });
    236 
    237 add_task(async function test_localtab_matches_override() {
    238  // We have an open tab to the same page on a remote device, only "switch to
    239  // tab" should appear as duplicate detection removed the remote one.
    240 
    241  // First set up Sync to have the page as a remote tab.
    242  configureEngine([
    243    {
    244      id: "mobile",
    245      tabs: [
    246        {
    247          urlHistory: ["http://foo.com/"],
    248          title: "An Example",
    249        },
    250      ],
    251    },
    252  ]);
    253 
    254  // Set up Places to think the tab is open locally.
    255  let uri = Services.io.newURI("http://foo.com/");
    256  await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]);
    257  await addOpenPages(uri, 1);
    258 
    259  let query = "ex";
    260  let context = createContext(query, { isPrivate: false });
    261  await check_results({
    262    context,
    263    matches: [
    264      makeSearchResult(context, {
    265        engineName: SUGGESTIONS_ENGINE_NAME,
    266        heuristic: true,
    267      }),
    268      makeTabSwitchResult(context, {
    269        uri: "http://foo.com/",
    270        title: "An Example",
    271      }),
    272    ],
    273  });
    274 
    275  await removeOpenPages(uri, 1);
    276  await cleanupPlaces();
    277 });
    278 
    279 add_task(async function test_remotetab_matches_override() {
    280  // If we have an history result to the same page, we should only get the
    281  // remote tab match.
    282  let url = "http://foo.remote.com/";
    283  // First set up Sync to have the page as a remote tab.
    284  configureEngine([
    285    {
    286      id: "mobile",
    287      tabs: [
    288        {
    289          urlHistory: [url],
    290          title: "An Example",
    291        },
    292      ],
    293    },
    294  ]);
    295 
    296  // Set up Places to think the tab is in history.
    297  await PlacesTestUtils.addVisits(url);
    298 
    299  let query = "ex";
    300  let context = createContext(query, { isPrivate: false });
    301  await check_results({
    302    context,
    303    matches: [
    304      makeSearchResult(context, {
    305        engineName: SUGGESTIONS_ENGINE_NAME,
    306        heuristic: true,
    307      }),
    308      makeRemoteTabResult(context, {
    309        uri: "http://foo.remote.com/",
    310        device: "My Phone",
    311        title: "An Example",
    312      }),
    313    ],
    314  });
    315 
    316  await cleanupPlaces();
    317 });
    318 
    319 add_task(async function test_mixed_result_types() {
    320  // In case we have many results, non-remote results should flex to the bottom.
    321  let url = "http://foo.remote.com/";
    322  let tabs = Array(6)
    323    .fill(0)
    324    .map((e, i) => ({
    325      urlHistory: [`${url}${i}`],
    326      title: "A title",
    327      lastUsed: Math.floor(Date.now() / 1000) - i * 86400, // i days ago.
    328    }));
    329  // First set up Sync to have the page as a remote tab.
    330  configureEngine([{ id: "mobile", tabs }]);
    331 
    332  // Register the page as an open tab.
    333  let openTabUrl = url + "openpage/";
    334  let uri = Services.io.newURI(openTabUrl);
    335  await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]);
    336  await addOpenPages(uri, 1);
    337 
    338  // Also add a local history result.
    339  let historyUrl = url + "history/";
    340  await PlacesTestUtils.addVisits(historyUrl);
    341 
    342  let query = "rem";
    343  let context = createContext(query, { isPrivate: false });
    344  await check_results({
    345    context,
    346    matches: [
    347      makeSearchResult(context, {
    348        engineName: SUGGESTIONS_ENGINE_NAME,
    349        heuristic: true,
    350      }),
    351      makeRemoteTabResult(context, {
    352        uri: "http://foo.remote.com/0",
    353        device: "My Phone",
    354        title: "A title",
    355        lastUsed: tabs[0].lastUsed,
    356      }),
    357      makeRemoteTabResult(context, {
    358        uri: "http://foo.remote.com/1",
    359        device: "My Phone",
    360        title: "A title",
    361        lastUsed: tabs[1].lastUsed,
    362      }),
    363      makeRemoteTabResult(context, {
    364        uri: "http://foo.remote.com/2",
    365        device: "My Phone",
    366        title: "A title",
    367        lastUsed: tabs[2].lastUsed,
    368      }),
    369      makeRemoteTabResult(context, {
    370        uri: "http://foo.remote.com/3",
    371        device: "My Phone",
    372        title: "A title",
    373        lastUsed: tabs[3].lastUsed,
    374      }),
    375      makeRemoteTabResult(context, {
    376        uri: "http://foo.remote.com/4",
    377        device: "My Phone",
    378        title: "A title",
    379        lastUsed: tabs[4].lastUsed,
    380      }),
    381      makeRemoteTabResult(context, {
    382        uri: "http://foo.remote.com/5",
    383        device: "My Phone",
    384        title: "A title",
    385        lastUsed: tabs[5].lastUsed,
    386      }),
    387      makeVisitResult(context, {
    388        uri: historyUrl,
    389        title: "test visit for " + historyUrl,
    390      }),
    391      makeTabSwitchResult(context, {
    392        uri: openTabUrl,
    393        title: "An Example",
    394      }),
    395    ],
    396  });
    397  await removeOpenPages(uri, 1);
    398  await cleanupPlaces();
    399 });
    400 
    401 add_task(async function test_many_remotetab_results() {
    402  let url = "http://foo.remote.com/";
    403  let tabs = Array(8)
    404    .fill(0)
    405    .map((e, i) => ({
    406      urlHistory: [`${url}${i}`],
    407      title: "A title",
    408      lastUsed: Math.floor(Date.now() / 1000) - i * 86400, // i days old.
    409    }));
    410 
    411  // First set up Sync to have the page as a remote tab.
    412  configureEngine([
    413    {
    414      id: "mobile",
    415      tabs,
    416    },
    417  ]);
    418 
    419  let query = "rem";
    420  let context = createContext(query, { isPrivate: false });
    421  await check_results({
    422    context,
    423    matches: [
    424      makeSearchResult(context, {
    425        engineName: SUGGESTIONS_ENGINE_NAME,
    426        heuristic: true,
    427      }),
    428      makeRemoteTabResult(context, {
    429        uri: "http://foo.remote.com/0",
    430        device: "My Phone",
    431        title: "A title",
    432        lastUsed: tabs[0].lastUsed,
    433      }),
    434      makeRemoteTabResult(context, {
    435        uri: "http://foo.remote.com/1",
    436        device: "My Phone",
    437        title: "A title",
    438        lastUsed: tabs[1].lastUsed,
    439      }),
    440      makeRemoteTabResult(context, {
    441        uri: "http://foo.remote.com/2",
    442        device: "My Phone",
    443        title: "A title",
    444        lastUsed: tabs[2].lastUsed,
    445      }),
    446      makeRemoteTabResult(context, {
    447        uri: "http://foo.remote.com/3",
    448        device: "My Phone",
    449        title: "A title",
    450        lastUsed: tabs[3].lastUsed,
    451      }),
    452      makeRemoteTabResult(context, {
    453        uri: "http://foo.remote.com/4",
    454        device: "My Phone",
    455        title: "A title",
    456        lastUsed: tabs[4].lastUsed,
    457      }),
    458      makeRemoteTabResult(context, {
    459        uri: "http://foo.remote.com/5",
    460        device: "My Phone",
    461        title: "A title",
    462        lastUsed: tabs[5].lastUsed,
    463      }),
    464      makeRemoteTabResult(context, {
    465        uri: "http://foo.remote.com/6",
    466        device: "My Phone",
    467        title: "A title",
    468        lastUsed: tabs[6].lastUsed,
    469      }),
    470      makeRemoteTabResult(context, {
    471        uri: "http://foo.remote.com/7",
    472        device: "My Phone",
    473        title: "A title",
    474        lastUsed: tabs[7].lastUsed,
    475      }),
    476    ],
    477  });
    478 });
    479 
    480 add_task(async function multiple_clients() {
    481  let url = "http://foo.remote.com/";
    482  let mobileTabs = Array(2)
    483    .fill(0)
    484    .map((e, i) => ({
    485      urlHistory: [`${url}mobile/${i}`],
    486      lastUsed: Date.now() / 1000 - 4 * 86400, // 4 days old (past threshold)
    487    }));
    488 
    489  let desktopTabs = Array(3)
    490    .fill(0)
    491    .map((e, i) => ({
    492      urlHistory: [`${url}desktop/${i}`],
    493      lastUsed: Date.now() / 1000 - 1, // Fresh tabs
    494    }));
    495 
    496  // mobileTabs has the most recent tab, making it the most recent client. The
    497  // rest of its tabs are stale. The tabs in desktopTabs are fresh, but not
    498  // as fresh as the most recent tab in mobileTab.
    499  mobileTabs.push({
    500    urlHistory: [`${url}mobile/fresh`],
    501    lastUsed: Date.now() / 1000,
    502  });
    503 
    504  configureEngine([
    505    {
    506      id: "mobile",
    507      tabs: mobileTabs,
    508    },
    509    {
    510      id: "desktop",
    511      tabs: desktopTabs,
    512    },
    513  ]);
    514 
    515  // We expect that we will show the recent tab from mobileTabs, then all the
    516  // tabs from desktopTabs, then the remaining tabs from mobileTabs.
    517  let query = "rem";
    518  let context = createContext(query, { isPrivate: false });
    519  await check_results({
    520    context,
    521    matches: [
    522      makeSearchResult(context, {
    523        engineName: SUGGESTIONS_ENGINE_NAME,
    524        heuristic: true,
    525      }),
    526      makeRemoteTabResult(context, {
    527        uri: "http://foo.remote.com/mobile/fresh",
    528        device: "My Phone",
    529        lastUsed: mobileTabs[2].lastUsed,
    530      }),
    531      makeRemoteTabResult(context, {
    532        uri: "http://foo.remote.com/desktop/0",
    533        device: "My Desktop",
    534        lastUsed: desktopTabs[0].lastUsed,
    535      }),
    536      makeRemoteTabResult(context, {
    537        uri: "http://foo.remote.com/desktop/1",
    538        device: "My Desktop",
    539        lastUsed: desktopTabs[1].lastUsed,
    540      }),
    541      makeRemoteTabResult(context, {
    542        uri: "http://foo.remote.com/desktop/2",
    543        device: "My Desktop",
    544        lastUsed: desktopTabs[2].lastUsed,
    545      }),
    546      makeRemoteTabResult(context, {
    547        uri: "http://foo.remote.com/mobile/0",
    548        device: "My Phone",
    549        lastUsed: mobileTabs[0].lastUsed,
    550      }),
    551      makeRemoteTabResult(context, {
    552        uri: "http://foo.remote.com/mobile/1",
    553        device: "My Phone",
    554        lastUsed: mobileTabs[1].lastUsed,
    555      }),
    556    ],
    557  });
    558 });
    559 
    560 add_task(async function test_restrictionCharacter() {
    561  let url = "http://foo.remote.com/";
    562  let tabs = Array(5)
    563    .fill(0)
    564    .map((e, i) => ({
    565      urlHistory: [`${url}${i}`],
    566      title: "A title",
    567      lastUsed: Math.floor(Date.now() / 1000) - i,
    568    }));
    569  configureEngine([
    570    {
    571      id: "mobile",
    572      tabs,
    573    },
    574  ]);
    575 
    576  // Also add an open page.
    577  let openTabUrl = url + "openpage/";
    578  let uri = Services.io.newURI(openTabUrl);
    579  await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]);
    580  await addOpenPages(uri, 1);
    581 
    582  // We expect the open tab to flex to the bottom.
    583  let query = UrlbarTokenizer.RESTRICT.OPENPAGE;
    584  let context = createContext(query, { isPrivate: false });
    585  await check_results({
    586    context,
    587    matches: [
    588      makeSearchResult(context, {
    589        engineName: SUGGESTIONS_ENGINE_NAME,
    590        heuristic: true,
    591      }),
    592      makeRemoteTabResult(context, {
    593        uri: "http://foo.remote.com/0",
    594        device: "My Phone",
    595        title: "A title",
    596        lastUsed: tabs[0].lastUsed,
    597      }),
    598      makeRemoteTabResult(context, {
    599        uri: "http://foo.remote.com/1",
    600        device: "My Phone",
    601        title: "A title",
    602        lastUsed: tabs[1].lastUsed,
    603      }),
    604      makeRemoteTabResult(context, {
    605        uri: "http://foo.remote.com/2",
    606        device: "My Phone",
    607        title: "A title",
    608        lastUsed: tabs[2].lastUsed,
    609      }),
    610      makeRemoteTabResult(context, {
    611        uri: "http://foo.remote.com/3",
    612        device: "My Phone",
    613        title: "A title",
    614        lastUsed: tabs[3].lastUsed,
    615      }),
    616      makeRemoteTabResult(context, {
    617        uri: "http://foo.remote.com/4",
    618        device: "My Phone",
    619        title: "A title",
    620        lastUsed: tabs[4].lastUsed,
    621      }),
    622      makeTabSwitchResult(context, {
    623        uri: openTabUrl,
    624        title: "An Example",
    625      }),
    626    ],
    627  });
    628  await removeOpenPages(uri, 1);
    629  await cleanupPlaces();
    630 });
    631 
    632 add_task(async function test_duplicate_remote_tabs() {
    633  let url = "http://foo.remote.com/";
    634  let tabs = Array(3)
    635    .fill(0)
    636    .map(() => ({
    637      urlHistory: [url],
    638      title: "A title",
    639      lastUsed: Math.floor(Date.now() / 1000),
    640    }));
    641  configureEngine([
    642    {
    643      id: "mobile",
    644      tabs,
    645    },
    646  ]);
    647 
    648  // We expect the duplicate tabs to be deduped.
    649  let query = UrlbarTokenizer.RESTRICT.OPENPAGE;
    650  let context = createContext(query, { isPrivate: false });
    651  await check_results({
    652    context,
    653    matches: [
    654      makeSearchResult(context, {
    655        engineName: SUGGESTIONS_ENGINE_NAME,
    656        heuristic: true,
    657      }),
    658      makeRemoteTabResult(context, {
    659        uri: "http://foo.remote.com/",
    660        device: "My Phone",
    661        title: "A title",
    662        lastUsed: tabs[0].lastUsed,
    663      }),
    664    ],
    665  });
    666 });