mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 16:28:38 +09:00
Rename Toolkit's webextensions component directory to better reflect what it is.
This commit is contained in:
parent
6955450f7a
commit
d7d16e7fbb
242 changed files with 4 additions and 2 deletions
|
|
@ -1,680 +0,0 @@
|
|||
/* 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";
|
||||
|
||||
/**
|
||||
* This module contains utilities and base classes for logic which is
|
||||
* common between the parent and child process, and in particular
|
||||
* between ExtensionParent.jsm and ExtensionChild.jsm.
|
||||
*/
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
/* exported ExtensionCommon */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ExtensionCommon"];
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
||||
"resource://gre/modules/MessageChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
||||
"resource://gre/modules/Schemas.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
var {
|
||||
EventEmitter,
|
||||
ExtensionError,
|
||||
SpreadArgs,
|
||||
getConsole,
|
||||
getInnerWindowID,
|
||||
getUniqueId,
|
||||
runSafeSync,
|
||||
runSafeSyncWithoutClone,
|
||||
instanceOf,
|
||||
} = ExtensionUtils;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", getConsole);
|
||||
|
||||
class BaseContext {
|
||||
constructor(envType, extension) {
|
||||
this.envType = envType;
|
||||
this.onClose = new Set();
|
||||
this.checkedLastError = false;
|
||||
this._lastError = null;
|
||||
this.contextId = getUniqueId();
|
||||
this.unloaded = false;
|
||||
this.extension = extension;
|
||||
this.jsonSandbox = null;
|
||||
this.active = true;
|
||||
this.incognito = null;
|
||||
this.messageManager = null;
|
||||
this.docShell = null;
|
||||
this.contentWindow = null;
|
||||
this.innerWindowID = 0;
|
||||
}
|
||||
|
||||
setContentWindow(contentWindow) {
|
||||
let {document} = contentWindow;
|
||||
let docShell = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell);
|
||||
|
||||
this.innerWindowID = getInnerWindowID(contentWindow);
|
||||
this.messageManager = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
|
||||
if (this.incognito == null) {
|
||||
this.incognito = PrivateBrowsingUtils.isContentWindowPrivate(contentWindow);
|
||||
}
|
||||
|
||||
MessageChannel.setupMessageManagers([this.messageManager]);
|
||||
|
||||
let onPageShow = event => {
|
||||
if (!event || event.target === document) {
|
||||
this.docShell = docShell;
|
||||
this.contentWindow = contentWindow;
|
||||
this.active = true;
|
||||
}
|
||||
};
|
||||
let onPageHide = event => {
|
||||
if (!event || event.target === document) {
|
||||
// Put this off until the next tick.
|
||||
Promise.resolve().then(() => {
|
||||
this.docShell = null;
|
||||
this.contentWindow = null;
|
||||
this.active = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onPageShow();
|
||||
contentWindow.addEventListener("pagehide", onPageHide, true);
|
||||
contentWindow.addEventListener("pageshow", onPageShow, true);
|
||||
this.callOnClose({
|
||||
close: () => {
|
||||
onPageHide();
|
||||
if (this.active) {
|
||||
contentWindow.removeEventListener("pagehide", onPageHide, true);
|
||||
contentWindow.removeEventListener("pageshow", onPageShow, true);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
get cloneScope() {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
get principal() {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
runSafe(...args) {
|
||||
if (this.unloaded) {
|
||||
Cu.reportError("context.runSafe called after context unloaded");
|
||||
} else if (!this.active) {
|
||||
Cu.reportError("context.runSafe called while context is inactive");
|
||||
} else {
|
||||
return runSafeSync(this, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
runSafeWithoutClone(...args) {
|
||||
if (this.unloaded) {
|
||||
Cu.reportError("context.runSafeWithoutClone called after context unloaded");
|
||||
} else if (!this.active) {
|
||||
Cu.reportError("context.runSafeWithoutClone called while context is inactive");
|
||||
} else {
|
||||
return runSafeSyncWithoutClone(...args);
|
||||
}
|
||||
}
|
||||
|
||||
checkLoadURL(url, options = {}) {
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
|
||||
let flags = ssm.STANDARD;
|
||||
if (!options.allowScript) {
|
||||
flags |= ssm.DISALLOW_SCRIPT;
|
||||
}
|
||||
if (!options.allowInheritsPrincipal) {
|
||||
flags |= ssm.DISALLOW_INHERIT_PRINCIPAL;
|
||||
}
|
||||
if (options.dontReportErrors) {
|
||||
flags |= ssm.DONT_REPORT_ERRORS;
|
||||
}
|
||||
|
||||
try {
|
||||
ssm.checkLoadURIStrWithPrincipal(this.principal, url, flags);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely call JSON.stringify() on an object that comes from an
|
||||
* extension.
|
||||
*
|
||||
* @param {array<any>} args Arguments for JSON.stringify()
|
||||
* @returns {string} The stringified representation of obj
|
||||
*/
|
||||
jsonStringify(...args) {
|
||||
if (!this.jsonSandbox) {
|
||||
this.jsonSandbox = Cu.Sandbox(this.principal, {
|
||||
sameZoneAs: this.cloneScope,
|
||||
wantXrays: false,
|
||||
});
|
||||
}
|
||||
|
||||
return Cu.waiveXrays(this.jsonSandbox.JSON).stringify(...args);
|
||||
}
|
||||
|
||||
callOnClose(obj) {
|
||||
this.onClose.add(obj);
|
||||
}
|
||||
|
||||
forgetOnClose(obj) {
|
||||
this.onClose.delete(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around MessageChannel.sendMessage which adds the extension ID
|
||||
* to the recipient object, and ensures replies are not processed after the
|
||||
* context has been unloaded.
|
||||
*
|
||||
* @param {nsIMessageManager} target
|
||||
* @param {string} messageName
|
||||
* @param {object} data
|
||||
* @param {object} [options]
|
||||
* @param {object} [options.sender]
|
||||
* @param {object} [options.recipient]
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
sendMessage(target, messageName, data, options = {}) {
|
||||
options.recipient = options.recipient || {};
|
||||
options.sender = options.sender || {};
|
||||
|
||||
options.recipient.extensionId = this.extension.id;
|
||||
options.sender.extensionId = this.extension.id;
|
||||
options.sender.contextId = this.contextId;
|
||||
|
||||
return MessageChannel.sendMessage(target, messageName, data, options);
|
||||
}
|
||||
|
||||
get lastError() {
|
||||
this.checkedLastError = true;
|
||||
return this._lastError;
|
||||
}
|
||||
|
||||
set lastError(val) {
|
||||
this.checkedLastError = false;
|
||||
this._lastError = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the given error object for use by the target scope. If
|
||||
* the target is an error object which belongs to that scope, it is
|
||||
* returned as-is. If it is an ordinary object with a `message`
|
||||
* property, it is converted into an error belonging to the target
|
||||
* scope. If it is an Error object which does *not* belong to the
|
||||
* clone scope, it is reported, and converted to an unexpected
|
||||
* exception error.
|
||||
*
|
||||
* @param {Error|object} error
|
||||
* @returns {Error}
|
||||
*/
|
||||
normalizeError(error) {
|
||||
if (error instanceof this.cloneScope.Error) {
|
||||
return error;
|
||||
}
|
||||
let message;
|
||||
if (instanceOf(error, "Object") || error instanceof ExtensionError) {
|
||||
message = error.message;
|
||||
} else if (typeof error == "object" &&
|
||||
this.principal.subsumes(Cu.getObjectPrincipal(error))) {
|
||||
message = error.message;
|
||||
} else {
|
||||
Cu.reportError(error);
|
||||
}
|
||||
message = message || "An unexpected error occurred";
|
||||
return new this.cloneScope.Error(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of `.lastError` to `error`, calls the given
|
||||
* callback, and reports an error if the value has not been checked
|
||||
* when the callback returns.
|
||||
*
|
||||
* @param {object} error An object with a `message` property. May
|
||||
* optionally be an `Error` object belonging to the target scope.
|
||||
* @param {function} callback The callback to call.
|
||||
* @returns {*} The return value of callback.
|
||||
*/
|
||||
withLastError(error, callback) {
|
||||
this.lastError = this.normalizeError(error);
|
||||
try {
|
||||
return callback();
|
||||
} finally {
|
||||
if (!this.checkedLastError) {
|
||||
Cu.reportError(`Unchecked lastError value: ${this.lastError}`);
|
||||
}
|
||||
this.lastError = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given promise so it can be safely returned to extension
|
||||
* code in this context.
|
||||
*
|
||||
* If `callback` is provided, however, it is used as a completion
|
||||
* function for the promise, and no promise is returned. In this case,
|
||||
* the callback is called when the promise resolves or rejects. In the
|
||||
* latter case, `lastError` is set to the rejection value, and the
|
||||
* callback function must check `browser.runtime.lastError` or
|
||||
* `extension.runtime.lastError` in order to prevent it being reported
|
||||
* to the console.
|
||||
*
|
||||
* @param {Promise} promise The promise with which to wrap the
|
||||
* callback. May resolve to a `SpreadArgs` instance, in which case
|
||||
* each element will be used as a separate argument.
|
||||
*
|
||||
* Unless the promise object belongs to the cloneScope global, its
|
||||
* resolution value is cloned into cloneScope prior to calling the
|
||||
* `callback` function or resolving the wrapped promise.
|
||||
*
|
||||
* @param {function} [callback] The callback function to wrap
|
||||
*
|
||||
* @returns {Promise|undefined} If callback is null, a promise object
|
||||
* belonging to the target scope. Otherwise, undefined.
|
||||
*/
|
||||
wrapPromise(promise, callback = null) {
|
||||
let runSafe = this.runSafe.bind(this);
|
||||
if (promise instanceof this.cloneScope.Promise) {
|
||||
runSafe = this.runSafeWithoutClone.bind(this);
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
promise.then(
|
||||
args => {
|
||||
if (this.unloaded) {
|
||||
dump(`Promise resolved after context unloaded\n`);
|
||||
} else if (!this.active) {
|
||||
dump(`Promise resolved while context is inactive\n`);
|
||||
} else if (args instanceof SpreadArgs) {
|
||||
runSafe(callback, ...args);
|
||||
} else {
|
||||
runSafe(callback, args);
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.withLastError(error, () => {
|
||||
if (this.unloaded) {
|
||||
dump(`Promise rejected after context unloaded\n`);
|
||||
} else if (!this.active) {
|
||||
dump(`Promise rejected while context is inactive\n`);
|
||||
} else {
|
||||
this.runSafeWithoutClone(callback);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return new this.cloneScope.Promise((resolve, reject) => {
|
||||
promise.then(
|
||||
value => {
|
||||
if (this.unloaded) {
|
||||
dump(`Promise resolved after context unloaded\n`);
|
||||
} else if (!this.active) {
|
||||
dump(`Promise resolved while context is inactive\n`);
|
||||
} else if (value instanceof SpreadArgs) {
|
||||
runSafe(resolve, value.length == 1 ? value[0] : value);
|
||||
} else {
|
||||
runSafe(resolve, value);
|
||||
}
|
||||
},
|
||||
value => {
|
||||
if (this.unloaded) {
|
||||
dump(`Promise rejected after context unloaded: ${value && value.message}\n`);
|
||||
} else if (!this.active) {
|
||||
dump(`Promise rejected while context is inactive: ${value && value.message}\n`);
|
||||
} else {
|
||||
this.runSafeWithoutClone(reject, this.normalizeError(value));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
unload() {
|
||||
this.unloaded = true;
|
||||
|
||||
MessageChannel.abortResponses({
|
||||
extensionId: this.extension.id,
|
||||
contextId: this.contextId,
|
||||
});
|
||||
|
||||
for (let obj of this.onClose) {
|
||||
obj.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple proxy for unload(), for use with callOnClose().
|
||||
*/
|
||||
close() {
|
||||
this.unload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that runs the implementation of a schema API. Instantiations of
|
||||
* this interfaces are used by Schemas.jsm.
|
||||
*
|
||||
* @interface
|
||||
*/
|
||||
class SchemaAPIInterface {
|
||||
/**
|
||||
* Calls this as a function that returns its return value.
|
||||
*
|
||||
* @abstract
|
||||
* @param {Array} args The parameters for the function.
|
||||
* @returns {*} The return value of the invoked function.
|
||||
*/
|
||||
callFunction(args) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls this as a function and ignores its return value.
|
||||
*
|
||||
* @abstract
|
||||
* @param {Array} args The parameters for the function.
|
||||
*/
|
||||
callFunctionNoReturn(args) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls this as a function that completes asynchronously.
|
||||
*
|
||||
* @abstract
|
||||
* @param {Array} args The parameters for the function.
|
||||
* @param {function(*)} [callback] The callback to be called when the function
|
||||
* completes.
|
||||
* @returns {Promise|undefined} Must be void if `callback` is set, and a
|
||||
* promise otherwise. The promise is resolved when the function completes.
|
||||
*/
|
||||
callAsyncFunction(args, callback) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of this as a property.
|
||||
*
|
||||
* @abstract
|
||||
* @returns {*} The value of the property.
|
||||
*/
|
||||
getProperty() {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the value to this as property.
|
||||
*
|
||||
* @abstract
|
||||
* @param {string} value The new value of the property.
|
||||
*/
|
||||
setProperty(value) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a `listener` to this as an event.
|
||||
*
|
||||
* @abstract
|
||||
* @param {function} listener The callback to be called when the event fires.
|
||||
* @param {Array} args Extra parameters for EventManager.addListener.
|
||||
* @see EventManager.addListener
|
||||
*/
|
||||
addListener(listener, args) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether `listener` is listening to this as an event.
|
||||
*
|
||||
* @abstract
|
||||
* @param {function} listener The event listener.
|
||||
* @returns {boolean} Whether `listener` is registered with this as an event.
|
||||
* @see EventManager.hasListener
|
||||
*/
|
||||
hasListener(listener) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters `listener` from this as an event.
|
||||
*
|
||||
* @abstract
|
||||
* @param {function} listener The event listener.
|
||||
* @see EventManager.removeListener
|
||||
*/
|
||||
removeListener(listener) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that runs a locally implemented API.
|
||||
*/
|
||||
class LocalAPIImplementation extends SchemaAPIInterface {
|
||||
/**
|
||||
* Constructs an implementation of the `name` method or property of `pathObj`.
|
||||
*
|
||||
* @param {object} pathObj The object containing the member with name `name`.
|
||||
* @param {string} name The name of the implemented member.
|
||||
* @param {BaseContext} context The context in which the schema is injected.
|
||||
*/
|
||||
constructor(pathObj, name, context) {
|
||||
super();
|
||||
this.pathObj = pathObj;
|
||||
this.name = name;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
callFunction(args) {
|
||||
return this.pathObj[this.name](...args);
|
||||
}
|
||||
|
||||
callFunctionNoReturn(args) {
|
||||
this.pathObj[this.name](...args);
|
||||
}
|
||||
|
||||
callAsyncFunction(args, callback) {
|
||||
let promise;
|
||||
try {
|
||||
promise = this.pathObj[this.name](...args) || Promise.resolve();
|
||||
} catch (e) {
|
||||
promise = Promise.reject(e);
|
||||
}
|
||||
return this.context.wrapPromise(promise, callback);
|
||||
}
|
||||
|
||||
getProperty() {
|
||||
return this.pathObj[this.name];
|
||||
}
|
||||
|
||||
setProperty(value) {
|
||||
this.pathObj[this.name] = value;
|
||||
}
|
||||
|
||||
addListener(listener, args) {
|
||||
try {
|
||||
this.pathObj[this.name].addListener.call(null, listener, ...args);
|
||||
} catch (e) {
|
||||
throw this.context.normalizeError(e);
|
||||
}
|
||||
}
|
||||
|
||||
hasListener(listener) {
|
||||
return this.pathObj[this.name].hasListener.call(null, listener);
|
||||
}
|
||||
|
||||
removeListener(listener) {
|
||||
this.pathObj[this.name].removeListener.call(null, listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This object loads the ext-*.js scripts that define the extension API.
|
||||
*
|
||||
* This class instance is shared with the scripts that it loads, so that the
|
||||
* ext-*.js scripts and the instantiator can communicate with each other.
|
||||
*/
|
||||
class SchemaAPIManager extends EventEmitter {
|
||||
/**
|
||||
* @param {string} processType
|
||||
* "main" - The main, one and only chrome browser process.
|
||||
* "addon" - An addon process.
|
||||
* "content" - A content process.
|
||||
*/
|
||||
constructor(processType) {
|
||||
super();
|
||||
this.processType = processType;
|
||||
this.global = this._createExtGlobal();
|
||||
this._scriptScopes = [];
|
||||
this._schemaApis = {
|
||||
addon_parent: [],
|
||||
addon_child: [],
|
||||
content_parent: [],
|
||||
content_child: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a global object that is used as the shared global for all ext-*.js
|
||||
* scripts that are loaded via `loadScript`.
|
||||
*
|
||||
* @returns {object} A sandbox that is used as the global by `loadScript`.
|
||||
*/
|
||||
_createExtGlobal() {
|
||||
let global = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), {
|
||||
wantXrays: false,
|
||||
sandboxName: `Namespace of ext-*.js scripts for ${this.processType}`,
|
||||
});
|
||||
|
||||
Object.assign(global, {global, Cc, Ci, Cu, Cr, XPCOMUtils, extensions: this});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(global, "console", getConsole);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(global, "require",
|
||||
"resource://devtools/shared/Loader.jsm");
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an ext-*.js script. The script runs in its own scope, if it wishes to
|
||||
* share state with another script it can assign to the `global` variable. If
|
||||
* it wishes to communicate with this API manager, use `extensions`.
|
||||
*
|
||||
* @param {string} scriptUrl The URL of the ext-*.js script.
|
||||
*/
|
||||
loadScript(scriptUrl) {
|
||||
// Create the object in the context of the sandbox so that the script runs
|
||||
// in the sandbox's context instead of here.
|
||||
let scope = Cu.createObjectIn(this.global);
|
||||
|
||||
Services.scriptloader.loadSubScript(scriptUrl, scope, "UTF-8");
|
||||
|
||||
// Save the scope to avoid it being garbage collected.
|
||||
this._scriptScopes.push(scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by an ext-*.js script to register an API.
|
||||
*
|
||||
* @param {string} namespace The API namespace.
|
||||
* Intended to match the namespace of the generated API, but not used at
|
||||
* the moment - see bugzil.la/1295774.
|
||||
* @param {string} envType Restricts the API to contexts that run in the
|
||||
* given environment. Must be one of the following:
|
||||
* - "addon_parent" - addon APIs that runs in the main process.
|
||||
* - "addon_child" - addon APIs that runs in an addon process.
|
||||
* - "content_parent" - content script APIs that runs in the main process.
|
||||
* - "content_child" - content script APIs that runs in a content process.
|
||||
* @param {function(BaseContext)} getAPI A function that returns an object
|
||||
* that will be merged with |chrome| and |browser|. The next example adds
|
||||
* the create, update and remove methods to the tabs API.
|
||||
*
|
||||
* registerSchemaAPI("tabs", "addon_parent", (context) => ({
|
||||
* tabs: { create, update },
|
||||
* }));
|
||||
* registerSchemaAPI("tabs", "addon_parent", (context) => ({
|
||||
* tabs: { remove },
|
||||
* }));
|
||||
*/
|
||||
registerSchemaAPI(namespace, envType, getAPI) {
|
||||
this._schemaApis[envType].push({namespace, getAPI});
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports all registered scripts to `obj`.
|
||||
*
|
||||
* @param {BaseContext} context The context for which the API bindings are
|
||||
* generated.
|
||||
* @param {object} obj The destination of the API.
|
||||
*/
|
||||
generateAPIs(context, obj) {
|
||||
let apis = this._schemaApis[context.envType];
|
||||
if (!apis) {
|
||||
Cu.reportError(`No APIs have been registered for ${context.envType}`);
|
||||
return;
|
||||
}
|
||||
SchemaAPIManager.generateAPIs(context, apis, obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mash together all the APIs from `apis` into `obj`.
|
||||
*
|
||||
* @param {BaseContext} context The context for which the API bindings are
|
||||
* generated.
|
||||
* @param {Array} apis A list of objects, see `registerSchemaAPI`.
|
||||
* @param {object} obj The destination of the API.
|
||||
*/
|
||||
static generateAPIs(context, apis, obj) {
|
||||
// Recursively copy properties from source to dest.
|
||||
function copy(dest, source) {
|
||||
for (let prop in source) {
|
||||
let desc = Object.getOwnPropertyDescriptor(source, prop);
|
||||
if (typeof(desc.value) == "object") {
|
||||
if (!(prop in dest)) {
|
||||
dest[prop] = {};
|
||||
}
|
||||
copy(dest[prop], source[prop]);
|
||||
} else {
|
||||
Object.defineProperty(dest, prop, desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let api of apis) {
|
||||
if (Schemas.checkPermissions(api.namespace, context.extension)) {
|
||||
api = api.getAPI(context);
|
||||
copy(obj, api);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ExtensionCommon = {
|
||||
BaseContext,
|
||||
LocalAPIImplementation,
|
||||
SchemaAPIInterface,
|
||||
SchemaAPIManager,
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue