blob: 15ad200416d37fadd04f5442d6ee37232b3a6b76 [file] [log] [blame]
/*******************************************************************************
* @license
* Copyright (c) 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
* License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
/*global window ArrayBuffer addEventListener removeEventListener self XMLHttpRequest define*/
(function() {
var global = this;
if (!global.define) {
global.define = function(f) {
global.orion = global.orion || {};
global.orion.PluginProvider = f();
global.eclipse = global.orion; // (deprecated) backward compatibility
delete global.define;
};
}
}());
define(function() {
function PluginProvider(headers) {
var _headers = headers;
var _services = [];
var _connected = false;
var _activePromises = {};
var _target = null;
function _publish(message) {
if (_target) {
if (typeof(ArrayBuffer) === "undefined") { //$NON-NLS-0$
message = JSON.stringify(message);
}
if (_target === self) {
_target.postMessage(message);
} else {
_target.postMessage(message, "*"); //$NON-NLS-0$
}
}
}
function _getPluginData() {
var services = [];
// we filter out the service implementation from the data
for (var i = 0; i < _services.length; i++) {
services.push({
serviceId: i,
names: _services[i].names,
methods: _services[i].methods,
properties: _services[i].properties
});
}
return {
headers: _headers || {},
services: services
};
}
function _jsonXMLHttpRequestReplacer(name, value) {
if (value && value instanceof XMLHttpRequest) {
return {
status: value.status,
statusText: value.statusText
};
}
return value;
}
function _serializeError(error) {
var result = error ? JSON.parse(JSON.stringify(error, _jsonXMLHttpRequestReplacer)) : error; // sanitizing Error object
if (error instanceof Error) {
result.__isError = true;
result.message = result.message || error.message;
result.name = result.name || error.name;
}
return result;
}
function _createListener(serviceId) {
return function(event) {
if (_connected) {
var message = {
serviceId: serviceId,
method: "dispatchEvent", //$NON-NLS-0$
params: [event]
};
_publish(message);
}
};
}
function _handleRequest(event) {
if (event.source !== _target) {
return;
}
var message = (typeof event.data !== "string" ? event.data : JSON.parse(event.data)); //$NON-NLS-0$
if (typeof message.cancel === "string") {
var promise = _activePromises[message.id];
if (promise) {
delete _activePromises[message.id];
if (promise.cancel) {
promise.cancel(message.cancel);
}
}
return;
}
var serviceId = message.serviceId;
var methodName = message.method;
var params = message.params;
var service = _services[serviceId];
var implementation = service.implementation;
var method = implementation[methodName];
var type;
if (methodName === "addEventListener") {
type = params[0];
service.listeners[type] = service.listeners[type] || _createListener(serviceId);
params = [type, service.listeners[type]];
} else if (methodName === "removeEventListener") {
type = params[0];
params = [type, service.listeners[type]];
delete service.listeners[type];
}
var response = {
id: message.id,
result: null,
error: null
};
try {
var promiseOrResult = method.apply(implementation, params);
if (promiseOrResult && typeof promiseOrResult.then === "function") { //$NON-NLS-0$
_activePromises[message.id] = promiseOrResult;
promiseOrResult.then(function(result) {
delete _activePromises[message.id];
response.result = result;
_publish(response);
}, function(error) {
if (_activePromises[message.id]) {
delete _activePromises[message.id];
response.error = _serializeError(error);
_publish(response);
}
}, function() {
_publish({
requestId: message.id,
method: "progress",
params: Array.prototype.slice.call(arguments)
}); //$NON-NLS-0$
});
} else {
response.result = promiseOrResult;
_publish(response);
}
} catch (error) {
response.error = _serializeError(error);
_publish(response);
}
}
this.updateHeaders = function(headers) {
if (_connected) {
throw new Error("Cannot update headers. Plugin Provider is connected");
}
_headers = headers;
};
this.registerService = function(names, implementation, properties) {
if (_connected) {
throw new Error("Cannot register service. Plugin Provider is connected");
}
if (typeof names === "string") {
names = [names];
} else if (!Array.isArray(names)) {
names = [];
}
var method = null;
var methods = [];
for (method in implementation) {
if (typeof implementation[method] === 'function') { //$NON-NLS-0$
methods.push(method);
}
}
var serviceId = _services.length;
_services[serviceId] = {
names: names,
methods: methods,
implementation: implementation,
properties: properties || {},
listeners: {}
};
};
this.registerServiceProvider = this.registerService; // (deprecated) backwards compatibility only
this.connect = function(callback, errback) {
if (_connected) {
if (callback) {
callback();
}
return;
}
if (typeof(window) === "undefined") { //$NON-NLS-0$
_target = self;
} else if (window !== window.parent) {
_target = window.parent;
} else if (window.opener !== null) {
_target = window.opener;
} else {
if (errback) {
errback("No valid plugin target");
}
return;
}
addEventListener("message", _handleRequest, false); //$NON-NLS-0$
var message = {
method: "plugin", //$NON-NLS-0$
params: [_getPluginData()]
};
_publish(message);
_connected = true;
if (callback) {
callback();
}
};
this.disconnect = function() {
if (_connected) {
removeEventListener("message", _handleRequest); //$NON-NLS-0$
_target = null;
_connected = false;
}
// Note: re-connecting is not currently supported
};
}
return PluginProvider;
});