Refuse to register second extension with same ID

If an extension attempts to register with the same ID as another
extension which has already registered, the new registration is refused.
If the extension is in a worker and no other extension is successfully
registered by that worker, the watchdog system will terminate the
"empty" worker.
This commit is contained in:
Christopher Willis-Ford 2017-10-31 11:32:21 -07:00
parent e4381b4693
commit 670e51d335
2 changed files with 47 additions and 9 deletions
src

View file

@ -55,6 +55,19 @@ class CentralDispatch extends SharedDispatch {
}
}
/**
* Unregister a service without regard to which object or worker might provide it.
* @param {string} service - the globally unique string provided to `setService` when the service was registered.
* @see {setService}
*/
clearService (service) {
if (this.services.hasOwnProperty(service)) {
delete this.services[service];
} else {
log.warn(`Central dispatch can't clear unknown service ${service}`);
}
}
/**
* Add a worker to the message dispatch system. The worker must implement a compatible message dispatch framework.
* The dispatcher will immediately attempt to "handshake" with the worker.

View file

@ -70,6 +70,13 @@ class ExtensionManager {
*/
this.pendingWorkers = [];
/**
* Set of loaded extension IDs. For built-in extensions the "URL" is the same as the ID; they differ in general.
* @type {Set.<string>}
* @private
*/
this._loadedExtensions = new Set();
/**
* Set of workers currently being monitored by `_startWorkerWatchdog`.
* @see {_startWorkerWatchdog}
@ -90,6 +97,17 @@ class ExtensionManager {
});
}
/**
* Check whether an extension is registered or is in the process of loading. This is intended to control loading or
* adding extensions so it may return `true` before the extension is ready to be used. Use the promise returned by
* `loadExtensionURL` if you need to wait until the extension is truly ready.
* @param {string} extensionID - the ID (not URL) of the extension.
* @returns {boolean} - true if loaded, false otherwise.
*/
isExtensionLoaded (extensionID) {
return this._loadedExtensions.has(extensionID);
}
/**
* Load an extension by URL or internal extension ID
* @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension
@ -170,15 +188,22 @@ class ExtensionManager {
*/
_registerExtensionInfo (serviceName, extensionInfo) {
extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo);
dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).then(
() => {
if (dispatch.callingWorker) {
this._stopWorkerWatchdog(dispatch.callingWorker);
}
},
e => {
log.error(`Failed to register primitives for extension "${extensionInfo.id}": ${e.message}`);
});
if (this.isExtensionLoaded(extensionInfo.id)) {
const message = `Ignoring attempt to load a second extension with ID ${extensionInfo.id}`;
log.warn(message);
dispatch.clearService(serviceName);
} else {
dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).then(
() => {
this._loadedExtensions.add(extensionInfo.id);
if (dispatch.callingWorker) {
this._stopWorkerWatchdog(dispatch.callingWorker);
}
},
e => {
log.error(`Failed to register primitives for extension "${extensionInfo.id}": ${e.message}`);
});
}
}
/**