Bug 1293298 - Implement events in SubTypes defined in the WebExtensions API schema files. draft
authorLuca Greco <lgreco@mozilla.com>
Thu, 01 Jun 2017 20:02:54 +0200
changeset 594894 2f0ddac027ece0ad32bc34a080bfbdc81d9d195b
parent 593303 a8f8e440d627d686fa8898483aa9c5da928a8fa4
child 633575 bb69947d8c89d1e26fa233cbc187cb5588c7dd34
push id64195
push userluca.greco@alcacoop.it
push dateThu, 15 Jun 2017 18:06:35 +0000
bugs1293298
milestone56.0a1
Bug 1293298 - Implement events in SubTypes defined in the WebExtensions API schema files. MozReview-Commit-ID: CAYaenfSih1
toolkit/components/extensions/Schemas.jsm
toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
--- a/toolkit/components/extensions/Schemas.jsm
+++ b/toolkit/components/extensions/Schemas.jsm
@@ -1348,16 +1348,17 @@ class StringType extends Type {
       return {
         descriptor: {value: obj},
       };
     }
   }
 }
 
 let FunctionEntry;
+let Event;
 let SubModuleType;
 
 class ObjectType extends Type {
   static get EXTRA_PROPERTIES() {
     return ["properties", "patternProperties", ...super.EXTRA_PROPERTIES];
   }
 
   static parseSchema(schema, path, extraProperties = []) {
@@ -1611,22 +1612,30 @@ SubModuleType = class SubModuleType exte
   static parseSchema(schema, path, extraProperties = []) {
     this.checkSchemaProperties(schema, path, extraProperties);
 
     // The path we pass in here is only used for error messages.
     path = [...path, schema.id];
     let functions = schema.functions.filter(fun => !fun.unsupported)
                           .map(fun => FunctionEntry.parseSchema(fun, path));
 
-    return new this(functions);
+    let events = [];
+
+    if (schema.events) {
+      events = schema.events.filter(event => !event.unsupported)
+                     .map(event => Event.parseSchema(event, path));
+    }
+
+    return new this(functions, events);
   }
 
-  constructor(functions) {
+  constructor(functions, events) {
     super();
     this.functions = functions;
+    this.events = events;
   }
 };
 
 class NumberType extends Type {
   normalize(value, context) {
     let r = this.normalizeBase("number", value, context);
     if (r.error) {
       return r;
@@ -1939,16 +1948,21 @@ class SubModuleProperty extends Entry {
     }
     let subpath = [...path, this.name];
 
     let functions = type.functions;
     for (let fun of functions) {
       context.injectInto(fun, obj, fun.name, subpath, ns);
     }
 
+    let events = type.events;
+    for (let event of events) {
+      context.injectInto(event, obj, event.name, subpath, ns);
+    }
+
     // TODO: Inject this.properties.
 
     return {
       descriptor: {value: obj},
       revoke() {
         let unwrapped = Cu.waiveXrays(obj);
         for (let fun of functions) {
           try {
@@ -2116,17 +2130,20 @@ FunctionEntry = class FunctionEntry exte
         apiImpl.revoke();
         apiImpl = null;
       },
     };
   }
 };
 
 // Represents an "event" defined in a schema namespace.
-class Event extends CallEntry {
+//
+// TODO(rpl): we should be able to remove the eslint-disable-line that follows
+// once Bug 1369722 has been fixed.
+Event = class Event extends CallEntry { // eslint-disable-line no-native-reassign
   static parseSchema(event, path) {
     let extraParameters = Array.from(event.extraParameters || [], param => ({
       type: Schemas.parseSchema(param, path, ["name", "optional", "default"]),
       name: param.name,
       optional: param.optional || false,
       default: param.default == undefined ? null : param.default,
     }));
 
@@ -2189,17 +2206,17 @@ class Event extends CallEntry {
 
         let unwrapped = Cu.waiveXrays(obj);
         delete unwrapped.addListener;
         delete unwrapped.removeListener;
         delete unwrapped.hasListener;
       },
     };
   }
-}
+};
 
 const TYPES = Object.freeze(Object.assign(Object.create(null), {
   any: AnyType,
   array: ArrayType,
   boolean: BooleanType,
   function: FunctionType,
   integer: IntegerType,
   number: NumberType,
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
@@ -1217,16 +1217,17 @@ let nestedNamespaceJson = [
     "namespace": "nested.namespace",
     "types": [
       {
         "id": "CustomType",
         "type": "object",
         "events": [
           {
             "name": "onEvent",
+            "type": "function",
           },
         ],
         "properties": {
           "url": {
             "type": "string",
           },
         },
         "functions": [
@@ -1281,27 +1282,37 @@ add_task(async function testNestedNamesp
      "The property is a function as expected");
 
   let {instanceOfCustomType} = root.nested.namespace;
 
   ok(instanceOfCustomType,
      "Got the expected instance of the CustomType defined in the schema");
   ok(instanceOfCustomType.functionOnCustomType,
      "Got the expected method in the CustomType instance");
+  ok(instanceOfCustomType.onEvent &&
+     instanceOfCustomType.onEvent.addListener &&
+     typeof instanceOfCustomType.onEvent.addListener == "function",
+     "Got the expected event defined in the CustomType instance");
 
-  // TODO: test support events and properties in a SubModuleType defined in the schema,
+  instanceOfCustomType.functionOnCustomType("param_value");
+  verify("call", "nested.namespace.instanceOfCustomType",
+         "functionOnCustomType", ["param_value"]);
+
+  let fakeListener = () => {};
+  instanceOfCustomType.onEvent.addListener(fakeListener);
+  verify("addListener", "nested.namespace.instanceOfCustomType",
+         "onEvent", [fakeListener, []]);
+  instanceOfCustomType.onEvent.removeListener(fakeListener);
+  verify("removeListener", "nested.namespace.instanceOfCustomType",
+         "onEvent", [fakeListener]);
+
+  // TODO: test support properties in a SubModuleType defined in the schema,
   // once implemented, e.g.:
-  //
-  // ok(instanceOfCustomType.url,
-  //    "Got the expected property defined in the CustomType instance)
-  //
-  // ok(instanceOfCustomType.onEvent &&
-  //    instanceOfCustomType.onEvent.addListener &&
-  //    typeof instanceOfCustomType.onEvent.addListener == "function",
-  //    "Got the expected event defined in the CustomType instance");
+  // ok("url" in instanceOfCustomType,
+  //   "Got the expected property defined in the CustomType instance");
 });
 
 let $importJson = [
   {
     namespace: "from_the",
     $import: "future",
   },
   {