Bug 1813648 - [devtools] Remove ActorClassWithSpec and all its dependencies. r=devtools-reviewers,jdescottes

Some actors weren't using any specification. They now have to pass a bare minimal spec object.
Unifying them to the regular Actor interface helps merging constructor with initialize
and instantiate all the actors without any code branch.

Differential Revision: https://phabricator.services.mozilla.com/D169359
This commit is contained in:
Alexandre Poirot
2023-02-14 10:34:19 +00:00
parent a6ffe0d5fb
commit 44e5012d2c
13 changed files with 35 additions and 110 deletions

View File

@@ -7,10 +7,9 @@ const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
class TestActor1 extends Actor {
constructor(conn, tab) {
super(conn);
super(conn, { typeName: "testOne", methods: [] });
this.tab = tab;
this.typeName = "testOne";
this.requestTypes = {
ping: TestActor1.prototype.onPing,
};

View File

@@ -35,18 +35,18 @@ It has two parts: a spec and an implementation. The spec would go somewhere like
The actor implementation would go somewhere like
`devtools/server/actors/hello-world.js` and would look like:
const protocol = require("devtools/shared/protocol");
const { Actor } = require("devtools/shared/protocol");
const {helloWorldSpec} = require("devtools/shared/specs/hello-world");
const HelloActor = protocol.ActorClassWithSpec(helloWorldSpec, {
initialize: function (conn) {
protocol.Actor.prototype.initialize.call(this, conn); // This is the worst part of heritage.
},
class HelloActor extends Actor {
constructor(conn) {
super(conn, helloWorldSpec);
}
sayHello: function () {
sayHello() {
return "hello";
},
});
}
}
// You also need to export the actor class in your module for discovery.
exports.HelloActor = HelloActor;
@@ -310,15 +310,17 @@ Probably the most common objects that need custom martialing are actors themselv
});
// implementation:
const ChildActor = protocol.ActorClassWithSpec(childActorSpec, {
initialize: function (conn, id) {
protocol.Actor.prototype.initialize.call(this, conn);
class ChildActor extends Actor {
constructor(conn, id) {
super(conn, childActorSpec);
this.greeting = "hello from " + id;
},
getGreeting: function () {
}
getGreeting() {
return this.greeting;
},
});
}
}
exports.ChildActor = ChildActor;
@@ -464,8 +466,8 @@ Here's how the implementation would look:
const EventEmitter = require("devtools/shared/event-emitter");
// In your protocol.ActorClassWithSpec definition:
giveGoodNews: function (news) {
// In your Actor class:
giveGoodNews(news) {
EventEmitter.emit(this, "good-news", news);
}

View File

@@ -40,9 +40,8 @@ add_task(async function() {
const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
class ConnectToFrameTestActor extends Actor {
constructor(conn, tab) {
super(conn);
super(conn, { typeName: "connectToFrameTest", methods: [] });
dump("instantiate test actor\n");
this.typeName = "connectToFrameTest";
this.requestTypes = {
hello: this.hello,
};

View File

@@ -11,9 +11,8 @@ const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
*/
class ErrorActor extends Actor {
constructor(conn, tab) {
super(conn);
super(conn, { typeName: "error", methods: [] });
this.tab = tab;
this.typeName = "error";
this.requestTypes = {
error: this.onError,
};

View File

@@ -7,9 +7,8 @@ const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
class PostInitGlobalActor extends Actor {
constructor(conn) {
super(conn);
super(conn, { typeName: "postInitGlobal", methods: [] });
this.typeName = "postInitGlobal";
this.requestTypes = {
ping: this.onPing,
};

View File

@@ -7,9 +7,8 @@ const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
class PostInitTargetScopedActor extends Actor {
constructor(conn) {
super(conn);
super(conn, { typeName: "postInitTargetScoped", methods: [] });
this.typeName = "postInitTargetScoped";
this.requestTypes = {
ping: this.onPing,
};

View File

@@ -7,9 +7,8 @@ const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
class PreInitGlobalActor extends Actor {
constructor(conn) {
super(conn);
super(conn, { typeName: "preInitGlobal", methods: [] });
this.typeName = "preInitGlobal";
this.requestTypes = {
ping: this.onPing,
};

View File

@@ -7,9 +7,8 @@ const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
class PreInitTargetScopedActor extends Actor {
constructor(conn) {
super(conn);
super(conn, { typeName: "preInitTargetScoped", methods: [] });
this.typeName = "preInitTargetScoped";
this.requestTypes = {
ping: this.onPing,
};

View File

@@ -11,9 +11,8 @@ const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
class TestActor extends Actor {
constructor(conn) {
super(conn);
super(conn, { typeName: "test", methods: [] });
this.typeName = "test";
this.requestTypes = {
hello: this.hello,
error: this.error,

View File

@@ -181,9 +181,8 @@ function TestForwardPrefix12OnlyRoot() {
const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
class EchoActor extends Actor {
constructor(conn) {
super(conn);
super(conn, { typeName: "EchoActor", methods: [] });
this.typeName = "EchoActor";
this.requestTypes = {
echo: EchoActor.prototype.onEcho,
};

View File

@@ -4,10 +4,7 @@
"use strict";
var {
Actor,
ActorClassWithSpec,
} = require("resource://devtools/shared/protocol/Actor.js");
var { Actor } = require("resource://devtools/shared/protocol/Actor.js");
var { Pool } = require("resource://devtools/shared/protocol/Pool.js");
var {
types,
@@ -28,7 +25,6 @@ const {
exports.Front = Front;
exports.Pool = Pool;
exports.Actor = Actor;
exports.ActorClassWithSpec = ActorClassWithSpec;
exports.types = types;
exports.generateActorSpec = generateActorSpec;
exports.FrontClassWithSpec = FrontClassWithSpec;

View File

@@ -4,7 +4,6 @@
"use strict";
const { extend } = require("resource://devtools/shared/extend.js");
var { Pool } = require("resource://devtools/shared/protocol/Pool.js");
/**
@@ -28,40 +27,18 @@ exports.actorSpecs = actorSpecs;
class Actor extends Pool {
constructor(conn, spec) {
super();
super(conn);
// Actors migrated to ES Classes, passed the specification via the constructor
if (spec) {
this.typeName = spec.typeName;
this.requestTypes = generateRequestTypes(spec);
this._actorSpec = spec;
}
this.initialize(conn);
}
// Existing Actors extending this class expect initialize to contain constructor logic.
// Bug 1813648: This method can be folded into constructor once all Actor are migrated to ES Classes.
initialize(conn) {
// Repeat Pool.constructor here as we can't call it from initialize
// This is to be removed once actors switch to es classes and are able to call
// Actor's contructor.
if (conn) {
this.conn = conn;
}
this.typeName = spec.typeName;
// Will contain the actor's ID
this.actorID = null;
// When the subclass is still using ActorClassWithSpec (i.e. not ES Classses)
// The spec is only registered in actorSpecs.
// This codepath can be removed once all actors are using ES Classes.
if (!this._actorSpec) {
this._actorSpec = actorSpecs.get(Object.getPrototypeOf(this));
}
this.requestTypes = generateRequestTypes(spec);
// Forward events to the connection.
if (this._actorSpec && this._actorSpec.events) {
for (const [name, request] of this._actorSpec.events.entries()) {
if (spec.events) {
for (const [name, request] of spec.events.entries()) {
this.on(name, (...args) => {
this._sendEvent(name, request, ...args);
});
@@ -278,44 +255,3 @@ var generateRequestTypes = function(actorSpec) {
return requestTypes;
};
exports.generateRequestTypes = generateRequestTypes;
var generateRequestHandlers = function(actorSpec, actorProto) {
actorProto.typeName = actorSpec.typeName;
// Generate request handlers for each method definition
actorProto.requestTypes = generateRequestTypes(actorSpec);
return actorProto;
};
/**
* Create an actor class for the given actor specification and prototype.
*
* @param object actorSpec
* The actor specification. Must have a 'typeName' property.
* @param object actorProto
* The actor prototype. Should have method definitions, can have event
* definitions.
*/
// bug 1813648: We can remove this codepath, generateRequestHandlers and actorSpecs WeakMap once all Actors use ES Classes
var ActorClassWithSpec = function(actorSpec, actorProto) {
if (!actorSpec.typeName) {
throw Error("Actor specification must have a typeName member.");
}
// Existing Actors are relying on the initialize instead of constructor methods.
const cls = function() {
const instance = Object.create(cls.prototype);
instance.initialize.apply(instance, arguments);
return instance;
};
cls.prototype = extend(
Actor.prototype,
generateRequestHandlers(actorSpec, actorProto)
);
actorSpecs.set(cls.prototype, actorSpec);
return cls;
};
exports.ActorClassWithSpec = ActorClassWithSpec;

View File

@@ -17,7 +17,7 @@ add_task(async function() {
front.destroy();
ok(front.isDestroyed(), "Front is destroyed");
const actor = new Actor();
const actor = new Actor(null, { typeName: "actor", methods: [] });
ok(
!actor.isDestroyed(),
"Blank actor with no actor ID is not considered as destroyed"