tor-browser

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

browser_events_dispatcher.js (17543B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /**
      7 * Check the basic behavior of on/off.
      8 */
      9 add_task(async function test_add_remove_event_listener() {
     10  const tab = await addTab("https://example.com/document-builder.sjs?html=tab");
     11  const browsingContext = tab.linkedBrowser.browsingContext;
     12  const contextDescriptor = {
     13    type: ContextDescriptorType.TopBrowsingContext,
     14    id: browsingContext.browserId,
     15  };
     16 
     17  const root = createRootMessageHandler("session-id-event");
     18  const monitoringEvents = await setupEventMonitoring(root);
     19  await emitTestEvent(root, browsingContext, monitoringEvents);
     20  is(await isSubscribed(root, browsingContext), false);
     21 
     22  info("Add an listener for eventemitter.testEvent");
     23  const events = [];
     24  const onEvent = (event, data) => events.push(data.text);
     25  await root.eventsDispatcher.on(
     26    "eventemitter.testEvent",
     27    contextDescriptor,
     28    onEvent
     29  );
     30  is(await isSubscribed(root, browsingContext), true);
     31 
     32  await emitTestEvent(root, browsingContext, monitoringEvents);
     33  is(events.length, 1);
     34 
     35  info(
     36    "Remove a listener for a callback not added before and check that the first one is still registered"
     37  );
     38  const anotherCallback = () => {};
     39  await root.eventsDispatcher.off(
     40    "eventemitter.testEvent",
     41    contextDescriptor,
     42    anotherCallback
     43  );
     44  is(await isSubscribed(root, browsingContext), true);
     45 
     46  await emitTestEvent(root, browsingContext, monitoringEvents);
     47  is(events.length, 2);
     48 
     49  info("Remove the listener for eventemitter.testEvent");
     50  await root.eventsDispatcher.off(
     51    "eventemitter.testEvent",
     52    contextDescriptor,
     53    onEvent
     54  );
     55  is(await isSubscribed(root, browsingContext), false);
     56 
     57  await emitTestEvent(root, browsingContext, monitoringEvents);
     58  is(events.length, 2);
     59 
     60  info("Add the listener for eventemitter.testEvent again");
     61  await root.eventsDispatcher.on(
     62    "eventemitter.testEvent",
     63    contextDescriptor,
     64    onEvent
     65  );
     66  is(await isSubscribed(root, browsingContext), true);
     67 
     68  await emitTestEvent(root, browsingContext, monitoringEvents);
     69  is(events.length, 3);
     70 
     71  info("Remove the listener for eventemitter.testEvent");
     72  await root.eventsDispatcher.off(
     73    "eventemitter.testEvent",
     74    contextDescriptor,
     75    onEvent
     76  );
     77  is(await isSubscribed(root, browsingContext), false);
     78 
     79  info("Remove the listener again to check the API will not throw");
     80  await root.eventsDispatcher.off(
     81    "eventemitter.testEvent",
     82    contextDescriptor,
     83    onEvent
     84  );
     85 
     86  root.destroy();
     87  gBrowser.removeTab(tab);
     88 });
     89 
     90 add_task(async function test_add_remove_event_listener_for_user_context() {
     91  const tab = await addTab("https://example.com/document-builder.sjs?html=tab");
     92  const browsingContext = tab.linkedBrowser.browsingContext;
     93  const contextDescriptor = {
     94    type: ContextDescriptorType.UserContext,
     95    id: browsingContext.originAttributes.userContextId,
     96  };
     97 
     98  const root = createRootMessageHandler("session-id-event");
     99  const monitoringEvents = await setupEventMonitoring(root);
    100  await emitTestEvent(root, browsingContext, monitoringEvents);
    101  is(await isSubscribed(root, browsingContext), false);
    102 
    103  info("Add an listener for eventemitter.testEvent");
    104  const events = [];
    105  const onEvent = (event, data) => events.push(data.text);
    106  await root.eventsDispatcher.on(
    107    "eventemitter.testEvent",
    108    contextDescriptor,
    109    onEvent
    110  );
    111  is(await isSubscribed(root, browsingContext), true);
    112 
    113  await emitTestEvent(root, browsingContext, monitoringEvents);
    114  is(events.length, 1);
    115 
    116  info("Remove the listener for eventemitter.testEvent");
    117  await root.eventsDispatcher.off(
    118    "eventemitter.testEvent",
    119    contextDescriptor,
    120    onEvent
    121  );
    122  is(await isSubscribed(root, browsingContext), false);
    123 
    124  root.destroy();
    125  gBrowser.removeTab(tab);
    126 });
    127 
    128 add_task(async function test_has_listener() {
    129  const tab1 = await addTab("https://example.com/document-builder.sjs?html=1");
    130  const browsingContext1 = tab1.linkedBrowser.browsingContext;
    131 
    132  const tab2 = await addTab("https://example.com/document-builder.sjs?html=2");
    133  const browsingContext2 = tab2.linkedBrowser.browsingContext;
    134 
    135  const contextDescriptor1 = {
    136    type: ContextDescriptorType.TopBrowsingContext,
    137    id: browsingContext1.browserId,
    138  };
    139  const contextDescriptor2 = {
    140    type: ContextDescriptorType.TopBrowsingContext,
    141    id: browsingContext2.browserId,
    142  };
    143 
    144  const root = createRootMessageHandler("session-id-event");
    145 
    146  // Shortcut for the EventsDispatcher.hasListener API.
    147  function hasListener(contextId) {
    148    return root.eventsDispatcher.hasListener("eventemitter.testEvent", {
    149      contextId,
    150    });
    151  }
    152 
    153  const onEvent = () => {};
    154  await root.eventsDispatcher.on(
    155    "eventemitter.testEvent",
    156    contextDescriptor1,
    157    onEvent
    158  );
    159  ok(hasListener(browsingContext1.id), "Has a listener for browsingContext1");
    160  ok(!hasListener(browsingContext2.id), "No listener for browsingContext2");
    161 
    162  await root.eventsDispatcher.on(
    163    "eventemitter.testEvent",
    164    contextDescriptor2,
    165    onEvent
    166  );
    167  ok(hasListener(browsingContext1.id), "Still a listener for browsingContext1");
    168  ok(hasListener(browsingContext2.id), "Has a listener for browsingContext2");
    169 
    170  await root.eventsDispatcher.off(
    171    "eventemitter.testEvent",
    172    contextDescriptor1,
    173    onEvent
    174  );
    175  ok(!hasListener(browsingContext1.id), "No listener for browsingContext1");
    176  ok(hasListener(browsingContext2.id), "Still a listener for browsingContext2");
    177 
    178  await root.eventsDispatcher.off(
    179    "eventemitter.testEvent",
    180    contextDescriptor2,
    181    onEvent
    182  );
    183  ok(!hasListener(browsingContext1.id), "No listener for browsingContext1");
    184  ok(!hasListener(browsingContext2.id), "No listener for browsingContext2");
    185 
    186  await root.eventsDispatcher.on(
    187    "eventemitter.testEvent",
    188    {
    189      type: ContextDescriptorType.All,
    190    },
    191    onEvent
    192  );
    193  ok(hasListener(browsingContext1.id), "Has a listener for browsingContext1");
    194  ok(hasListener(browsingContext2.id), "Has a listener for browsingContext2");
    195 
    196  await root.eventsDispatcher.off(
    197    "eventemitter.testEvent",
    198    {
    199      type: ContextDescriptorType.All,
    200    },
    201    onEvent
    202  );
    203  ok(!hasListener(browsingContext1.id), "No listener for browsingContext1");
    204  ok(!hasListener(browsingContext2.id), "No listener for browsingContext2");
    205 
    206  root.destroy();
    207  gBrowser.removeTab(tab2);
    208  gBrowser.removeTab(tab1);
    209 });
    210 
    211 /**
    212 * Check that two callbacks can subscribe to the same event in the same context
    213 * in parallel.
    214 */
    215 add_task(async function test_two_callbacks() {
    216  const tab = await addTab("https://example.com/document-builder.sjs?html=tab");
    217  const browsingContext = tab.linkedBrowser.browsingContext;
    218  const contextDescriptor = {
    219    type: ContextDescriptorType.TopBrowsingContext,
    220    id: browsingContext.browserId,
    221  };
    222 
    223  const root = createRootMessageHandler("session-id-event");
    224  const monitoringEvents = await setupEventMonitoring(root);
    225 
    226  info("Add an listener for eventemitter.testEvent");
    227  const events = [];
    228  const onEvent = (event, data) => events.push(data.text);
    229  await root.eventsDispatcher.on(
    230    "eventemitter.testEvent",
    231    contextDescriptor,
    232    onEvent
    233  );
    234 
    235  await emitTestEvent(root, browsingContext, monitoringEvents);
    236  is(events.length, 1);
    237 
    238  info("Add another listener for eventemitter.testEvent");
    239  const otherevents = [];
    240  const otherCallback = (event, data) => otherevents.push(data.text);
    241  await root.eventsDispatcher.on(
    242    "eventemitter.testEvent",
    243    contextDescriptor,
    244    otherCallback
    245  );
    246  is(await isSubscribed(root, browsingContext), true);
    247 
    248  await emitTestEvent(root, browsingContext, monitoringEvents);
    249  is(events.length, 2);
    250  is(otherevents.length, 1);
    251 
    252  info("Remove the other listener for eventemitter.testEvent");
    253  await root.eventsDispatcher.off(
    254    "eventemitter.testEvent",
    255    contextDescriptor,
    256    otherCallback
    257  );
    258  is(await isSubscribed(root, browsingContext), true);
    259 
    260  await emitTestEvent(root, browsingContext, monitoringEvents);
    261  is(events.length, 3);
    262  is(otherevents.length, 1);
    263 
    264  info("Remove the first listener for eventemitter.testEvent");
    265  await root.eventsDispatcher.off(
    266    "eventemitter.testEvent",
    267    contextDescriptor,
    268    onEvent
    269  );
    270  is(await isSubscribed(root, browsingContext), false);
    271 
    272  await emitTestEvent(root, browsingContext, monitoringEvents);
    273  is(events.length, 3);
    274  is(otherevents.length, 1);
    275 
    276  root.destroy();
    277  gBrowser.removeTab(tab);
    278 });
    279 
    280 /**
    281 * Check that two callbacks can subscribe to the same event in the two contexts.
    282 */
    283 add_task(async function test_two_contexts() {
    284  const tab1 = await addTab("https://example.com/document-builder.sjs?html=1");
    285  const browsingContext1 = tab1.linkedBrowser.browsingContext;
    286 
    287  const tab2 = await addTab("https://example.com/document-builder.sjs?html=2");
    288  const browsingContext2 = tab2.linkedBrowser.browsingContext;
    289 
    290  const contextDescriptor1 = {
    291    type: ContextDescriptorType.TopBrowsingContext,
    292    id: browsingContext1.browserId,
    293  };
    294  const contextDescriptor2 = {
    295    type: ContextDescriptorType.TopBrowsingContext,
    296    id: browsingContext2.browserId,
    297  };
    298 
    299  const root = createRootMessageHandler("session-id-event");
    300 
    301  const monitoringEvents = await setupEventMonitoring(root);
    302 
    303  const events1 = [];
    304  const onEvent1 = (event, data) => events1.push(data.text);
    305  await root.eventsDispatcher.on(
    306    "eventemitter.testEvent",
    307    contextDescriptor1,
    308    onEvent1
    309  );
    310  is(await isSubscribed(root, browsingContext1), true);
    311  is(await isSubscribed(root, browsingContext2), false);
    312 
    313  const events2 = [];
    314  const onEvent2 = (event, data) => events2.push(data.text);
    315  await root.eventsDispatcher.on(
    316    "eventemitter.testEvent",
    317    contextDescriptor2,
    318    onEvent2
    319  );
    320  is(await isSubscribed(root, browsingContext1), true);
    321  is(await isSubscribed(root, browsingContext2), true);
    322 
    323  await emitTestEvent(root, browsingContext1, monitoringEvents);
    324  is(events1.length, 1);
    325  is(events2.length, 0);
    326 
    327  await emitTestEvent(root, browsingContext2, monitoringEvents);
    328  is(events1.length, 1);
    329  is(events2.length, 1);
    330 
    331  await root.eventsDispatcher.off(
    332    "eventemitter.testEvent",
    333    contextDescriptor1,
    334    onEvent1
    335  );
    336  is(await isSubscribed(root, browsingContext1), false);
    337  is(await isSubscribed(root, browsingContext2), true);
    338 
    339  // No event expected here since the module for browsingContext1 is no longer
    340  // subscribed
    341  await emitTestEvent(root, browsingContext1, monitoringEvents);
    342  is(events1.length, 1);
    343  is(events2.length, 1);
    344 
    345  // Whereas the module for browsingContext2 is still subscribed
    346  await emitTestEvent(root, browsingContext2, monitoringEvents);
    347  is(events1.length, 1);
    348  is(events2.length, 2);
    349 
    350  await root.eventsDispatcher.off(
    351    "eventemitter.testEvent",
    352    contextDescriptor2,
    353    onEvent2
    354  );
    355  is(await isSubscribed(root, browsingContext1), false);
    356  is(await isSubscribed(root, browsingContext2), false);
    357 
    358  await emitTestEvent(root, browsingContext1, monitoringEvents);
    359  await emitTestEvent(root, browsingContext2, monitoringEvents);
    360  is(events1.length, 1);
    361  is(events2.length, 2);
    362 
    363  root.destroy();
    364  gBrowser.removeTab(tab2);
    365  gBrowser.removeTab(tab1);
    366 });
    367 
    368 /**
    369 * Check that adding and removing first listener for the specific context and then
    370 * for the global context works as expected.
    371 */
    372 add_task(
    373  async function test_remove_context_event_listener_and_then_global_event_listener() {
    374    const tab = await addTab(
    375      "https://example.com/document-builder.sjs?html=tab"
    376    );
    377    const browsingContext = tab.linkedBrowser.browsingContext;
    378    const contextDescriptor = {
    379      type: ContextDescriptorType.TopBrowsingContext,
    380      id: browsingContext.browserId,
    381    };
    382    const contextDescriptorAll = {
    383      type: ContextDescriptorType.All,
    384    };
    385 
    386    const root = createRootMessageHandler("session-id-event");
    387    const monitoringEvents = await setupEventMonitoring(root);
    388    await emitTestEvent(root, browsingContext, monitoringEvents);
    389    is(await isSubscribed(root, browsingContext), false);
    390 
    391    info("Add an listener for eventemitter.testEvent");
    392    const events = [];
    393    const onEvent = (event, data) => events.push(data.text);
    394    await root.eventsDispatcher.on(
    395      "eventemitter.testEvent",
    396      contextDescriptor,
    397      onEvent
    398    );
    399    is(await isSubscribed(root, browsingContext), true);
    400 
    401    await emitTestEvent(root, browsingContext, monitoringEvents);
    402    is(events.length, 1);
    403 
    404    info(
    405      "Add another listener for eventemitter.testEvent, using global context"
    406    );
    407    const eventsAll = [];
    408    const onEventAll = (event, data) => eventsAll.push(data.text);
    409    await root.eventsDispatcher.on(
    410      "eventemitter.testEvent",
    411      contextDescriptorAll,
    412      onEventAll
    413    );
    414    await emitTestEvent(root, browsingContext, monitoringEvents);
    415    is(eventsAll.length, 1);
    416    is(events.length, 2);
    417 
    418    info("Remove the first listener for eventemitter.testEvent");
    419    await root.eventsDispatcher.off(
    420      "eventemitter.testEvent",
    421      contextDescriptor,
    422      onEvent
    423    );
    424 
    425    info("Check that we are still subscribed to eventemitter.testEvent");
    426    is(await isSubscribed(root, browsingContext), true);
    427    await emitTestEvent(root, browsingContext, monitoringEvents);
    428    is(eventsAll.length, 2);
    429    is(events.length, 2);
    430 
    431    await root.eventsDispatcher.off(
    432      "eventemitter.testEvent",
    433      contextDescriptorAll,
    434      onEventAll
    435    );
    436    is(await isSubscribed(root, browsingContext), false);
    437    await emitTestEvent(root, browsingContext, monitoringEvents);
    438    is(eventsAll.length, 2);
    439    is(events.length, 2);
    440 
    441    root.destroy();
    442    gBrowser.removeTab(tab);
    443  }
    444 );
    445 
    446 /**
    447 * Check that adding and removing first listener for the global context and then
    448 * for the specific context works as expected.
    449 */
    450 add_task(
    451  async function test_global_event_listener_and_then_remove_context_event_listener() {
    452    const tab = await addTab(
    453      "https://example.com/document-builder.sjs?html=tab"
    454    );
    455    const browsingContext = tab.linkedBrowser.browsingContext;
    456    const contextDescriptor = {
    457      type: ContextDescriptorType.TopBrowsingContext,
    458      id: browsingContext.browserId,
    459    };
    460    const contextDescriptorAll = {
    461      type: ContextDescriptorType.All,
    462    };
    463 
    464    const root = createRootMessageHandler("session-id-event");
    465    const monitoringEvents = await setupEventMonitoring(root);
    466    await emitTestEvent(root, browsingContext, monitoringEvents);
    467    is(await isSubscribed(root, browsingContext), false);
    468 
    469    info("Add an listener for eventemitter.testEvent");
    470    const events = [];
    471    const onEvent = (event, data) => events.push(data.text);
    472    await root.eventsDispatcher.on(
    473      "eventemitter.testEvent",
    474      contextDescriptor,
    475      onEvent
    476    );
    477    is(await isSubscribed(root, browsingContext), true);
    478 
    479    await emitTestEvent(root, browsingContext, monitoringEvents);
    480    is(events.length, 1);
    481 
    482    info(
    483      "Add another listener for eventemitter.testEvent, using global context"
    484    );
    485    const eventsAll = [];
    486    const onEventAll = (event, data) => eventsAll.push(data.text);
    487    await root.eventsDispatcher.on(
    488      "eventemitter.testEvent",
    489      contextDescriptorAll,
    490      onEventAll
    491    );
    492    await emitTestEvent(root, browsingContext, monitoringEvents);
    493    is(eventsAll.length, 1);
    494    is(events.length, 2);
    495 
    496    info("Remove the global listener for eventemitter.testEvent");
    497    await root.eventsDispatcher.off(
    498      "eventemitter.testEvent",
    499      contextDescriptorAll,
    500      onEventAll
    501    );
    502 
    503    info(
    504      "Check that we are still subscribed to eventemitter.testEvent for the specific context"
    505    );
    506    is(await isSubscribed(root, browsingContext), true);
    507    await emitTestEvent(root, browsingContext, monitoringEvents);
    508    is(eventsAll.length, 1);
    509    is(events.length, 3);
    510 
    511    await root.eventsDispatcher.off(
    512      "eventemitter.testEvent",
    513      contextDescriptor,
    514      onEvent
    515    );
    516 
    517    is(await isSubscribed(root, browsingContext), false);
    518    await emitTestEvent(root, browsingContext, monitoringEvents);
    519    is(eventsAll.length, 1);
    520    is(events.length, 3);
    521 
    522    root.destroy();
    523    gBrowser.removeTab(tab);
    524  }
    525 );
    526 
    527 async function setupEventMonitoring(root) {
    528  const monitoringEvents = [];
    529  const onMonitoringEvent = (event, data) => monitoringEvents.push(data.text);
    530  root.on("eventemitter.monitoringEvent", onMonitoringEvent);
    531 
    532  registerCleanupFunction(() =>
    533    root.off("eventemitter.monitoringEvent", onMonitoringEvent)
    534  );
    535 
    536  return monitoringEvents;
    537 }
    538 
    539 async function emitTestEvent(root, browsingContext, monitoringEvents) {
    540  const count = monitoringEvents.length;
    541  info("Call eventemitter.emitTestEvent");
    542  await root.handleCommand({
    543    moduleName: "eventemitter",
    544    commandName: "emitTestEvent",
    545    destination: {
    546      type: WindowGlobalMessageHandler.type,
    547      id: browsingContext.id,
    548    },
    549  });
    550 
    551  // The monitoring event is always emitted, regardless of the status of the
    552  // module. Wait for catching this event before resuming the assertions.
    553  info("Wait for the monitoring event");
    554  await BrowserTestUtils.waitForCondition(
    555    () => monitoringEvents.length >= count + 1
    556  );
    557  is(monitoringEvents.length, count + 1);
    558 }
    559 
    560 function isSubscribed(root, browsingContext) {
    561  info("Call eventemitter.isSubscribed");
    562  return root.handleCommand({
    563    moduleName: "eventemitter",
    564    commandName: "isSubscribed",
    565    destination: {
    566      type: WindowGlobalMessageHandler.type,
    567      id: browsingContext.id,
    568    },
    569  });
    570 }