mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
9494d02df8
In a following patch, all DevTools moz.build files will use DevToolsModules to install JS modules at a path that corresponds directly to their source tree location. Here we rewrite all require and import calls to match the new location that these files are installed to.
320 lines
9.4 KiB
JavaScript
320 lines
9.4 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const { Task } = require("resource://gre/modules/Task.jsm");
|
|
const {
|
|
method, Arg, Option, RetVal, Front, FrontClass, Actor, ActorClass
|
|
} = require("devtools/server/protocol");
|
|
const events = require("sdk/event/core");
|
|
const { createSystem } = require("gcli/system");
|
|
|
|
/**
|
|
* Manage remote connections that want to talk to GCLI
|
|
*/
|
|
const GcliActor = ActorClass({
|
|
typeName: "gcli",
|
|
|
|
events: {
|
|
"commands-changed" : {
|
|
type: "commandsChanged"
|
|
}
|
|
},
|
|
|
|
initialize: function(conn, tabActor) {
|
|
Actor.prototype.initialize.call(this, conn);
|
|
|
|
this._commandsChanged = this._commandsChanged.bind(this);
|
|
|
|
this._tabActor = tabActor;
|
|
this._requisitionPromise = undefined; // see _getRequisition()
|
|
},
|
|
|
|
disconnect: function() {
|
|
return this.destroy();
|
|
},
|
|
|
|
destroy: function() {
|
|
Actor.prototype.destroy.call(this);
|
|
|
|
// If _getRequisition has not been called, just bail quickly
|
|
if (this._requisitionPromise == null) {
|
|
this._commandsChanged = undefined;
|
|
this._tabActor = undefined;
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return this._getRequisition().then(requisition => {
|
|
requisition.destroy();
|
|
|
|
this._system.commands.onCommandsChange.remove(this._commandsChanged);
|
|
this._system.destroy();
|
|
this._system = undefined;
|
|
|
|
this._requisitionPromise = undefined;
|
|
this._tabActor = undefined;
|
|
|
|
this._commandsChanged = undefined;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Load a module into the requisition
|
|
*/
|
|
_testOnly_addItemsByModule: method(function(names) {
|
|
return this._getRequisition().then(requisition => {
|
|
return requisition.system.addItemsByModule(names);
|
|
});
|
|
}, {
|
|
request: {
|
|
customProps: Arg(0, "array:string")
|
|
}
|
|
}),
|
|
|
|
/**
|
|
* Unload a module from the requisition
|
|
*/
|
|
_testOnly_removeItemsByModule: method(function(names) {
|
|
return this._getRequisition().then(requisition => {
|
|
return requisition.system.removeItemsByModule(names);
|
|
});
|
|
}, {
|
|
request: {
|
|
customProps: Arg(0, "array:string")
|
|
}
|
|
}),
|
|
|
|
/**
|
|
* Retrieve a list of the remotely executable commands
|
|
* @param customProps Array of strings containing additional properties which,
|
|
* if specified in the command spec, will be included in the JSON. Normally we
|
|
* transfer only the properties required for GCLI to function.
|
|
*/
|
|
specs: method(function(customProps) {
|
|
return this._getRequisition().then(requisition => {
|
|
return requisition.system.commands.getCommandSpecs(customProps);
|
|
});
|
|
}, {
|
|
request: {
|
|
customProps: Arg(0, "nullable:array:string")
|
|
},
|
|
response: {
|
|
value: RetVal("array:json")
|
|
}
|
|
}),
|
|
|
|
/**
|
|
* Execute a GCLI command
|
|
* @return a promise of an object with the following properties:
|
|
* - data: The output of the command
|
|
* - type: The type of the data to allow selection of a converter
|
|
* - error: True if the output was considered an error
|
|
*/
|
|
execute: method(function(typed) {
|
|
return this._getRequisition().then(requisition => {
|
|
return requisition.updateExec(typed).then(output => output.toJson());
|
|
});
|
|
}, {
|
|
request: {
|
|
typed: Arg(0, "string") // The command string
|
|
},
|
|
response: RetVal("json")
|
|
}),
|
|
|
|
/**
|
|
* Get the state of an input string. i.e. requisition.getStateData()
|
|
*/
|
|
state: method(function(typed, start, rank) {
|
|
return this._getRequisition().then(requisition => {
|
|
return requisition.update(typed).then(() => {
|
|
return requisition.getStateData(start, rank);
|
|
});
|
|
});
|
|
}, {
|
|
request: {
|
|
typed: Arg(0, "string"), // The command string
|
|
start: Arg(1, "number"), // Cursor start position
|
|
rank: Arg(2, "number") // The prediction offset (# times UP/DOWN pressed)
|
|
},
|
|
response: RetVal("json")
|
|
}),
|
|
|
|
/**
|
|
* Call type.parse to check validity. Used by the remote type
|
|
* @return a promise of an object with the following properties:
|
|
* - status: Of of the following strings: VALID|INCOMPLETE|ERROR
|
|
* - message: The message to display to the user
|
|
* - predictions: An array of suggested values for the given parameter
|
|
*/
|
|
parseType: method(function(typed, paramName) {
|
|
return this._getRequisition().then(requisition => {
|
|
return requisition.update(typed).then(() => {
|
|
let assignment = requisition.getAssignment(paramName);
|
|
return Promise.resolve(assignment.predictions).then(predictions => {
|
|
return {
|
|
status: assignment.getStatus().toString(),
|
|
message: assignment.message,
|
|
predictions: predictions
|
|
};
|
|
});
|
|
});
|
|
});
|
|
}, {
|
|
request: {
|
|
typed: Arg(0, "string"), // The command string
|
|
paramName: Arg(1, "string") // The name of the parameter to parse
|
|
},
|
|
response: RetVal("json")
|
|
}),
|
|
|
|
/**
|
|
* Get the incremented/decremented value of some type
|
|
* @return a promise of a string containing the new argument text
|
|
*/
|
|
nudgeType: method(function(typed, by, paramName) {
|
|
return this.requisition.update(typed).then(() => {
|
|
const assignment = this.requisition.getAssignment(paramName);
|
|
return this.requisition.nudge(assignment, by).then(() => {
|
|
return assignment.arg == null ? undefined : assignment.arg.text;
|
|
});
|
|
});
|
|
}, {
|
|
request: {
|
|
typed: Arg(0, "string"), // The command string
|
|
by: Arg(1, "number"), // +1/-1 for increment / decrement
|
|
paramName: Arg(2, "string") // The name of the parameter to parse
|
|
},
|
|
response: RetVal("string")
|
|
}),
|
|
|
|
/**
|
|
* Perform a lookup on a selection type to get the allowed values
|
|
*/
|
|
getSelectionLookup: method(function(commandName, paramName) {
|
|
return this._getRequisition().then(requisition => {
|
|
const command = requisition.system.commands.get(commandName);
|
|
if (command == null) {
|
|
throw new Error("No command called '" + commandName + "'");
|
|
}
|
|
|
|
let type;
|
|
command.params.forEach(param => {
|
|
if (param.name === paramName) {
|
|
type = param.type;
|
|
}
|
|
});
|
|
|
|
if (type == null) {
|
|
throw new Error("No parameter called '" + paramName + "' in '" +
|
|
commandName + "'");
|
|
}
|
|
|
|
const reply = type.getLookup(requisition.executionContext);
|
|
return Promise.resolve(reply).then(lookup => {
|
|
// lookup returns an array of objects with name/value properties and
|
|
// the values might not be JSONable, so remove them
|
|
return lookup.map(info => ({ name: info.name }));
|
|
});
|
|
});
|
|
}, {
|
|
request: {
|
|
commandName: Arg(0, "string"), // The command containing the parameter in question
|
|
paramName: Arg(1, "string"), // The name of the parameter
|
|
},
|
|
response: RetVal("json")
|
|
}),
|
|
|
|
/**
|
|
* Lazy init for a Requisition
|
|
*/
|
|
_getRequisition: function() {
|
|
if (this._tabActor == null) {
|
|
throw new Error('GcliActor used post-destroy');
|
|
}
|
|
|
|
if (this._requisitionPromise != null) {
|
|
return this._requisitionPromise;
|
|
}
|
|
|
|
const Requisition = require("gcli/cli").Requisition;
|
|
const tabActor = this._tabActor;
|
|
|
|
this._system = createSystem({ location: "server" });
|
|
this._system.commands.onCommandsChange.add(this._commandsChanged);
|
|
|
|
const gcliInit = require("devtools/shared/gcli/commands/index");
|
|
gcliInit.addAllItemsByModule(this._system);
|
|
|
|
// this._requisitionPromise should be created synchronously with the call
|
|
// to _getRequisition so that destroy can tell whether there is an async
|
|
// init in progress
|
|
this._requisitionPromise = this._system.load().then(() => {
|
|
const environment = {
|
|
get chromeWindow() {
|
|
throw new Error("environment.chromeWindow is not available in runAt:server commands");
|
|
},
|
|
|
|
get chromeDocument() {
|
|
throw new Error("environment.chromeDocument is not available in runAt:server commands");
|
|
},
|
|
|
|
get window() {
|
|
return tabActor.window;
|
|
},
|
|
|
|
get document() {
|
|
return tabActor.window && tabActor.window.document;
|
|
}
|
|
};
|
|
|
|
return new Requisition(this._system, { environment: environment });
|
|
});
|
|
|
|
return this._requisitionPromise;
|
|
},
|
|
|
|
/**
|
|
* Pass events from requisition.system.commands.onCommandsChange upwards
|
|
*/
|
|
_commandsChanged: function() {
|
|
events.emit(this, "commands-changed");
|
|
},
|
|
});
|
|
|
|
exports.GcliActor = GcliActor;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
const GcliFront = exports.GcliFront = FrontClass(GcliActor, {
|
|
initialize: function(client, tabForm) {
|
|
Front.prototype.initialize.call(this, client);
|
|
this.actorID = tabForm.gcliActor;
|
|
|
|
// XXX: This is the first actor type in its hierarchy to use the protocol
|
|
// library, so we're going to self-own on the client side for now.
|
|
this.manage(this);
|
|
},
|
|
});
|
|
|
|
// A cache of created fronts: WeakMap<Client, Front>
|
|
const knownFronts = new WeakMap();
|
|
|
|
/**
|
|
* Create a GcliFront only when needed (returns a promise)
|
|
* For notes on target.makeRemote(), see
|
|
* https://bugzilla.mozilla.org/show_bug.cgi?id=1016330#c7
|
|
*/
|
|
exports.GcliFront.create = function(target) {
|
|
return target.makeRemote().then(() => {
|
|
let front = knownFronts.get(target.client);
|
|
if (front == null && target.form.gcliActor != null) {
|
|
front = new GcliFront(target.client, target.form);
|
|
knownFronts.set(target.client, front);
|
|
}
|
|
return front;
|
|
});
|
|
};
|