tor-browser

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

test_protocol_children.js (17784B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 /* eslint-disable max-nested-callbacks */
      4 
      5 "use strict";
      6 
      7 /**
      8 * Test simple requests using the protocol helpers.
      9 */
     10 const protocol = require("resource://devtools/shared/protocol.js");
     11 const { types, Arg, RetVal } = protocol;
     12 
     13 // Predeclaring the actor type so that it can be used in the
     14 // implementation of the child actor.
     15 types.addActorType("childActor");
     16 types.addActorType("otherChildActor");
     17 types.addPolymorphicType("polytype", ["childActor", "otherChildActor"]);
     18 
     19 const childSpec = protocol.generateActorSpec({
     20  typeName: "childActor",
     21 
     22  events: {
     23    event1: {
     24      a: Arg(0),
     25      b: Arg(1),
     26      c: Arg(2),
     27    },
     28    event2: {
     29      a: Arg(0),
     30      b: Arg(1),
     31      c: Arg(2),
     32    },
     33    "named-event": {
     34      type: "namedEvent",
     35      a: Arg(0),
     36      b: Arg(1),
     37      c: Arg(2),
     38    },
     39    "object-event": {
     40      type: "objectEvent",
     41      detail: Arg(0, "childActor#actorid"),
     42    },
     43    "array-object-event": {
     44      type: "arrayObjectEvent",
     45      detail: Arg(0, "array:childActor#actorid"),
     46    },
     47  },
     48 
     49  methods: {
     50    echo: {
     51      request: { str: Arg(0) },
     52      response: { str: RetVal("string") },
     53    },
     54    getDetail1: {
     55      response: {
     56        child: RetVal("childActor#actorid"),
     57      },
     58    },
     59    getDetail2: {
     60      response: {
     61        child: RetVal("childActor#actorid"),
     62      },
     63    },
     64    getIDDetail: {
     65      response: {
     66        idDetail: RetVal("childActor#actorid"),
     67      },
     68    },
     69    getIntArray: {
     70      request: { inputArray: Arg(0, "array:number") },
     71      response: {
     72        intArray: RetVal("array:number"),
     73      },
     74    },
     75    getSibling: {
     76      request: { id: Arg(0) },
     77      response: { sibling: RetVal("childActor") },
     78    },
     79    emitEvents: {
     80      response: { value: RetVal("string") },
     81    },
     82    release: {
     83      release: true,
     84    },
     85  },
     86 });
     87 
     88 class ChildActor extends protocol.Actor {
     89  constructor(conn, id) {
     90    super(conn, childSpec);
     91    this.childID = id;
     92  }
     93 
     94  // Actors returned by this actor should be owned by the root actor.
     95  marshallPool() {
     96    return this.getParent();
     97  }
     98 
     99  toString() {
    100    return "[ChildActor " + this.childID + "]";
    101  }
    102 
    103  destroy() {
    104    super.destroy();
    105    this.destroyed = true;
    106  }
    107 
    108  form() {
    109    return {
    110      actor: this.actorID,
    111      childID: this.childID,
    112    };
    113  }
    114 
    115  echo(str) {
    116    return str;
    117  }
    118 
    119  getDetail1() {
    120    return this;
    121  }
    122 
    123  getDetail2() {
    124    return this;
    125  }
    126 
    127  getIDDetail() {
    128    return this;
    129  }
    130 
    131  getIntArray(inputArray) {
    132    // Test that protocol.js converts an iterator to an array.
    133    const f = function* () {
    134      for (const i of inputArray) {
    135        yield 2 * i;
    136      }
    137    };
    138    return f();
    139  }
    140 
    141  getSibling(id) {
    142    return this.getParent().getChild(id);
    143  }
    144 
    145  emitEvents() {
    146    this.emit("event1", 1, 2, 3);
    147    this.emit("event2", 4, 5, 6);
    148    this.emit("named-event", 1, 2, 3);
    149    this.emit("object-event", this);
    150    this.emit("array-object-event", [this]);
    151    return "correct response";
    152  }
    153 
    154  release() {}
    155 }
    156 
    157 class ChildFront extends protocol.FrontClassWithSpec(childSpec) {
    158  constructor(client, targetFront, parentFront) {
    159    super(client, targetFront, parentFront);
    160    this._parentFront = parentFront;
    161 
    162    this.before("event1", this.onEvent1.bind(this));
    163    this.before("event2", this.onEvent2a.bind(this));
    164    this.on("event2", this.onEvent2b.bind(this));
    165  }
    166 
    167  destroy() {
    168    this.destroyed = true;
    169    // Call parent's destroy, which may be re-entrant and recall this function
    170    this._parentFront.destroy();
    171    super.destroy();
    172  }
    173 
    174  marshallPool() {
    175    return this.getParent();
    176  }
    177 
    178  toString() {
    179    return "[child front " + this.childID + "]";
    180  }
    181 
    182  form(form) {
    183    this.childID = form.childID;
    184  }
    185 
    186  onEvent1(a, b, c) {
    187    this.event1arg3 = c;
    188  }
    189 
    190  onEvent2a(a, b, c) {
    191    return Promise.resolve().then(() => {
    192      this.event2arg3 = c;
    193    });
    194  }
    195 
    196  onEvent2b(a, b) {
    197    this.event2arg2 = b;
    198  }
    199 }
    200 protocol.registerFront(ChildFront);
    201 
    202 const otherChildSpec = protocol.generateActorSpec({
    203  typeName: "otherChildActor",
    204  methods: {
    205    getOtherChild: {
    206      request: {},
    207      response: { sibling: RetVal("otherChildActor") },
    208    },
    209  },
    210  events: {},
    211 });
    212 
    213 class OtherChildActor extends protocol.Actor {
    214  constructor(conn) {
    215    super(conn, otherChildSpec);
    216  }
    217 
    218  getOtherChild() {
    219    return new OtherChildActor(this.conn);
    220  }
    221 }
    222 
    223 class OtherChildFront extends protocol.FrontClassWithSpec(otherChildSpec) {}
    224 protocol.registerFront(OtherChildFront);
    225 
    226 types.addDictType("manyChildrenDict", {
    227  child5: "childActor",
    228  more: "array:childActor",
    229 });
    230 
    231 const rootSpec = protocol.generateActorSpec({
    232  typeName: "root",
    233 
    234  methods: {
    235    getChild: {
    236      request: { str: Arg(0) },
    237      response: { actor: RetVal("childActor") },
    238    },
    239    getOtherChild: {
    240      request: {},
    241      response: { sibling: RetVal("otherChildActor") },
    242    },
    243    getChildren: {
    244      request: { ids: Arg(0, "array:string") },
    245      response: { children: RetVal("array:childActor") },
    246    },
    247    getChildren2: {
    248      request: { ids: Arg(0, "array:childActor") },
    249      response: { children: RetVal("array:childActor") },
    250    },
    251    getManyChildren: {
    252      response: RetVal("manyChildrenDict"),
    253    },
    254    getPolymorphism: {
    255      request: { id: Arg(0, "number") },
    256      response: { child: RetVal("polytype") },
    257    },
    258    requestPolymorphism: {
    259      request: {
    260        id: Arg(0, "number"),
    261        actor: Arg(1, "polytype"),
    262      },
    263      response: { child: RetVal("polytype") },
    264    },
    265  },
    266 });
    267 
    268 let rootActor = null;
    269 class RootActor extends protocol.Actor {
    270  constructor(conn) {
    271    super(conn, rootSpec);
    272 
    273    rootActor = this;
    274    this.actorID = "root";
    275    this._children = {};
    276  }
    277 
    278  toString() {
    279    return "[root actor]";
    280  }
    281 
    282  sayHello() {
    283    return {
    284      from: "root",
    285      applicationType: "xpcshell-tests",
    286      traits: [],
    287    };
    288  }
    289 
    290  getChild(id) {
    291    if (id in this._children) {
    292      return this._children[id];
    293    }
    294    const child = new ChildActor(this.conn, id);
    295    this._children[id] = child;
    296    return child;
    297  }
    298 
    299  // Other child actor won't all be own by the root actor
    300  // and can have their own children
    301  getOtherChild() {
    302    return new OtherChildActor(this.conn);
    303  }
    304 
    305  getChildren(ids) {
    306    return ids.map(id => this.getChild(id));
    307  }
    308 
    309  getChildren2(ids) {
    310    const f = function* () {
    311      for (const c of ids) {
    312        yield c;
    313      }
    314    };
    315    return f();
    316  }
    317 
    318  getManyChildren() {
    319    return {
    320      // note that this isn't in the specialization array.
    321      foo: "bar",
    322      child5: this.getChild("child5"),
    323      more: [this.getChild("child6"), this.getChild("child7")],
    324    };
    325  }
    326 
    327  getPolymorphism(id) {
    328    if (id == 0) {
    329      return new ChildActor(this.conn, id);
    330    } else if (id == 1) {
    331      return new OtherChildActor(this.conn);
    332    }
    333    throw new Error("Unexpected id");
    334  }
    335 
    336  requestPolymorphism(id, actor) {
    337    if (id == 0 && actor instanceof ChildActor) {
    338      return actor;
    339    } else if (id == 1 && actor instanceof OtherChildActor) {
    340      return actor;
    341    }
    342    throw new Error("Unexpected id or actor");
    343  }
    344 }
    345 
    346 class RootFront extends protocol.FrontClassWithSpec(rootSpec) {
    347  constructor(client, targetFront, parentFront) {
    348    super(client, targetFront, parentFront);
    349    this.actorID = "root";
    350    // Root actor owns itself.
    351    this.manage(this);
    352  }
    353 
    354  toString() {
    355    return "[root front]";
    356  }
    357 
    358  connect() {}
    359 }
    360 
    361 let rootFront, childFront;
    362 function expectRootChildren(size) {
    363  Assert.equal(rootActor._poolMap.size, size);
    364  Assert.equal(rootFront._poolMap.size, size + 1);
    365  if (childFront) {
    366    Assert.equal(childFront._poolMap.size, 0);
    367  }
    368 }
    369 protocol.registerFront(RootFront);
    370 
    371 function childrenOfType(pool, type) {
    372  const children = [...rootFront.poolChildren()];
    373  return children.filter(child => child instanceof type);
    374 }
    375 
    376 add_task(async function () {
    377  DevToolsServer.createRootActor = conn => {
    378    return new RootActor(conn);
    379  };
    380  DevToolsServer.init();
    381 
    382  const trace = connectPipeTracing();
    383  const client = new DevToolsClient(trace);
    384  const [applicationType] = await client.connect();
    385  trace.expectReceive({
    386    from: "<actorid>",
    387    applicationType: "xpcshell-tests",
    388    traits: [],
    389  });
    390  Assert.equal(applicationType, "xpcshell-tests");
    391 
    392  rootFront = client.mainRoot;
    393 
    394  await testSimpleChildren(trace);
    395  await testDetail(trace);
    396  await testSibling(trace);
    397  await testEvents(trace);
    398  await testManyChildren(trace);
    399  await testGenerator(trace);
    400  await testPolymorphism(trace);
    401  await testUnmanageChildren(trace);
    402  // Execute that assertion very last as it destroy the root front and actor
    403  await testDestroy(trace);
    404 
    405  await client.close();
    406 });
    407 
    408 async function testSimpleChildren(trace) {
    409  childFront = await rootFront.getChild("child1");
    410  trace.expectSend({ type: "getChild", str: "child1", to: "<actorid>" });
    411  trace.expectReceive({ actor: "<actorid>", from: "<actorid>" });
    412 
    413  Assert.ok(childFront instanceof ChildFront);
    414  Assert.equal(childFront.childID, "child1");
    415  expectRootChildren(1);
    416 
    417  // Request the child again, make sure the same is returned.
    418  let ret = await rootFront.getChild("child1");
    419  trace.expectSend({ type: "getChild", str: "child1", to: "<actorid>" });
    420  trace.expectReceive({ actor: "<actorid>", from: "<actorid>" });
    421 
    422  expectRootChildren(1);
    423  Assert.strictEqual(ret, childFront);
    424 
    425  ret = await childFront.echo("hello");
    426  trace.expectSend({ type: "echo", str: "hello", to: "<actorid>" });
    427  trace.expectReceive({ str: "hello", from: "<actorid>" });
    428 
    429  Assert.equal(ret, "hello");
    430 }
    431 
    432 async function testDetail(trace) {
    433  let ret = await childFront.getDetail1();
    434  trace.expectSend({ type: "getDetail1", to: "<actorid>" });
    435  trace.expectReceive({ child: childFront.actorID, from: "<actorid>" });
    436  Assert.strictEqual(ret, childFront);
    437 
    438  ret = await childFront.getDetail2();
    439  trace.expectSend({ type: "getDetail2", to: "<actorid>" });
    440  trace.expectReceive({ child: childFront.actorID, from: "<actorid>" });
    441  Assert.strictEqual(ret, childFront);
    442 
    443  ret = await childFront.getIDDetail();
    444  trace.expectSend({ type: "getIDDetail", to: "<actorid>" });
    445  trace.expectReceive({
    446    idDetail: childFront.actorID,
    447    from: "<actorid>",
    448  });
    449  Assert.strictEqual(ret, childFront);
    450 }
    451 
    452 async function testSibling(trace) {
    453  await childFront.getSibling("siblingID");
    454  trace.expectSend({
    455    type: "getSibling",
    456    id: "siblingID",
    457    to: "<actorid>",
    458  });
    459  trace.expectReceive({
    460    sibling: { actor: "<actorid>", childID: "siblingID" },
    461    from: "<actorid>",
    462  });
    463 
    464  expectRootChildren(2);
    465 }
    466 
    467 async function testEvents(trace) {
    468  const ret = await rootFront.getChildren(["child1", "child2"]);
    469  trace.expectSend({
    470    type: "getChildren",
    471    ids: ["child1", "child2"],
    472    to: "<actorid>",
    473  });
    474  trace.expectReceive({
    475    children: [
    476      { actor: "<actorid>", childID: "child1" },
    477      { actor: "<actorid>", childID: "child2" },
    478    ],
    479    from: "<actorid>",
    480  });
    481 
    482  expectRootChildren(3);
    483  Assert.strictEqual(ret[0], childFront);
    484  Assert.notStrictEqual(ret[1], childFront);
    485  Assert.ok(ret[1] instanceof ChildFront);
    486 
    487  // On both children, listen to events.  We're only
    488  // going to trigger events on the first child, so an event
    489  // triggered on the second should cause immediate failures.
    490 
    491  const set = new Set([
    492    "event1",
    493    "event2",
    494    "named-event",
    495    "object-event",
    496    "array-object-event",
    497  ]);
    498 
    499  childFront.on("event1", (a, b, c) => {
    500    Assert.equal(a, 1);
    501    Assert.equal(b, 2);
    502    Assert.equal(c, 3);
    503    // Verify that the pre-event handler was called.
    504    Assert.equal(childFront.event1arg3, 3);
    505    set.delete("event1");
    506  });
    507  childFront.on("event2", (a, b, c) => {
    508    Assert.equal(a, 4);
    509    Assert.equal(b, 5);
    510    Assert.equal(c, 6);
    511    // Verify that the async pre-event handler was called,
    512    // setting the property before this handler was called.
    513    Assert.equal(childFront.event2arg3, 6);
    514    // And check that the sync preEvent with the same name is also
    515    // executed
    516    Assert.equal(childFront.event2arg2, 5);
    517    set.delete("event2");
    518  });
    519  childFront.on("named-event", (a, b, c) => {
    520    Assert.equal(a, 1);
    521    Assert.equal(b, 2);
    522    Assert.equal(c, 3);
    523    set.delete("named-event");
    524  });
    525  childFront.on("object-event", obj => {
    526    Assert.strictEqual(obj, childFront);
    527    set.delete("object-event");
    528  });
    529  childFront.on("array-object-event", array => {
    530    Assert.strictEqual(array[0], childFront);
    531    set.delete("array-object-event");
    532  });
    533 
    534  const fail = function () {
    535    do_throw("Unexpected event");
    536  };
    537  ret[1].on("event1", fail);
    538  ret[1].on("event2", fail);
    539  ret[1].on("named-event", fail);
    540  ret[1].on("object-event", fail);
    541  ret[1].on("array-object-event", fail);
    542 
    543  await childFront.emitEvents();
    544  trace.expectSend({ type: "emitEvents", to: "<actorid>" });
    545  trace.expectReceive({
    546    type: "event1",
    547    a: 1,
    548    b: 2,
    549    c: 3,
    550    from: "<actorid>",
    551  });
    552  trace.expectReceive({
    553    type: "event2",
    554    a: 4,
    555    b: 5,
    556    c: 6,
    557    from: "<actorid>",
    558  });
    559  trace.expectReceive({
    560    type: "namedEvent",
    561    a: 1,
    562    b: 2,
    563    c: 3,
    564    from: "<actorid>",
    565  });
    566  trace.expectReceive({
    567    type: "objectEvent",
    568    detail: childFront.actorID,
    569    from: "<actorid>",
    570  });
    571  trace.expectReceive({
    572    type: "arrayObjectEvent",
    573    detail: [childFront.actorID],
    574    from: "<actorid>",
    575  });
    576  trace.expectReceive({ value: "correct response", from: "<actorid>" });
    577 
    578  Assert.equal(set.size, 0);
    579 }
    580 
    581 async function testManyChildren(trace) {
    582  const ret = await rootFront.getManyChildren();
    583  trace.expectSend({ type: "getManyChildren", to: "<actorid>" });
    584  trace.expectReceive({
    585    foo: "bar",
    586    child5: { actor: "<actorid>", childID: "child5" },
    587    more: [
    588      { actor: "<actorid>", childID: "child6" },
    589      { actor: "<actorid>", childID: "child7" },
    590    ],
    591    from: "<actorid>",
    592  });
    593 
    594  // Check all the crazy stuff we did in getManyChildren
    595  Assert.equal(ret.foo, "bar");
    596  Assert.equal(ret.child5.childID, "child5");
    597  Assert.equal(ret.more[0].childID, "child6");
    598  Assert.equal(ret.more[1].childID, "child7");
    599 }
    600 
    601 async function testGenerator() {
    602  // Test accepting a generator.
    603  const f = function* () {
    604    for (const i of [1, 2, 3, 4, 5]) {
    605      yield i;
    606    }
    607  };
    608  let ret = await childFront.getIntArray(f());
    609  Assert.equal(ret.length, 5);
    610  const expected = [2, 4, 6, 8, 10];
    611  for (let i = 0; i < 5; ++i) {
    612    Assert.equal(ret[i], expected[i]);
    613  }
    614 
    615  const ids = await rootFront.getChildren(["child1", "child2"]);
    616  const f2 = function* () {
    617    for (const id of ids) {
    618      yield id;
    619    }
    620  };
    621  ret = await rootFront.getChildren2(f2());
    622  Assert.equal(ret.length, 2);
    623  Assert.strictEqual(ret[0], childFront);
    624  Assert.notStrictEqual(ret[1], childFront);
    625  Assert.ok(ret[1] instanceof ChildFront);
    626 }
    627 
    628 async function testPolymorphism() {
    629  // Check polymorphic types returned by an actor
    630  const firstChild = await rootFront.getPolymorphism(0);
    631  Assert.ok(firstChild instanceof ChildFront);
    632 
    633  // Check polymorphic types passed to a front
    634  const sameFirstChild = await rootFront.requestPolymorphism(0, firstChild);
    635  Assert.ok(sameFirstChild instanceof ChildFront);
    636  Assert.equal(sameFirstChild, firstChild);
    637 
    638  // Same with the second possible type
    639  const secondChild = await rootFront.getPolymorphism(1);
    640  Assert.ok(secondChild instanceof OtherChildFront);
    641 
    642  const sameSecondChild = await rootFront.requestPolymorphism(1, secondChild);
    643  Assert.ok(sameSecondChild instanceof OtherChildFront);
    644  Assert.equal(sameSecondChild, secondChild);
    645 
    646  // Check that any other type is rejected
    647  Assert.throws(() => {
    648    rootFront.requestPolymorphism(0, null);
    649  }, /Was expecting one of these actors 'childActor,otherChildActor' but instead got an empty value/);
    650  Assert.throws(() => {
    651    rootFront.requestPolymorphism(0, 42);
    652  }, /Was expecting one of these actors 'childActor,otherChildActor' but instead got value: '42'/);
    653  Assert.throws(() => {
    654    rootFront.requestPolymorphism(0, rootFront);
    655  }, /Was expecting one of these actors 'childActor,otherChildActor' but instead got an actor of type: 'root'/);
    656 }
    657 
    658 async function testUnmanageChildren() {
    659  // There is already one front of type OtherChildFront
    660  Assert.equal(childrenOfType(rootFront, OtherChildFront).length, 1);
    661 
    662  // Create another front of type OtherChildFront
    663  const front = await rootFront.getPolymorphism(1);
    664  Assert.ok(front instanceof OtherChildFront);
    665  Assert.equal(childrenOfType(rootFront, OtherChildFront).length, 2);
    666 
    667  // Remove all fronts of type OtherChildFront
    668  rootFront.unmanageChildren(OtherChildFront);
    669  Assert.ok(
    670    !front.isDestroyed(),
    671    "Unmanaged front is not considered as destroyed"
    672  );
    673  Assert.equal(childrenOfType(rootFront, OtherChildFront).length, 0);
    674 }
    675 
    676 async function testDestroy() {
    677  const front = await rootFront.getOtherChild();
    678  const otherChildFront = await front.getOtherChild();
    679  Assert.equal(
    680    otherChildFront.getParent(),
    681    front,
    682    "the child is a children of first front"
    683  );
    684 
    685  front.destroy();
    686  Assert.ok(front.isDestroyed(), "sibling is correctly reported as destroyed");
    687  Assert.ok(!front.getParent(), "sibling has no more parent declared");
    688  Assert.ok(otherChildFront.isDestroyed(), "the child is also destroyed");
    689  Assert.ok(
    690    !otherChildFront.getParent(),
    691    "the child also has no more parent declared"
    692  );
    693  Assert.ok(
    694    !otherChildFront.parentPool,
    695    "the child also has its parentPool attribute nullified"
    696  );
    697 
    698  // Verify that re-entrant Front.destroy doesn't throw, nor loop
    699  // Execute that very last as it will destroy the root actor and front
    700  const sibling = await childFront.getSibling("siblingID");
    701  sibling.destroy();
    702 }