mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-04 11:26:09 +00:00
189 lines
5.6 KiB
JavaScript
189 lines
5.6 KiB
JavaScript
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set sts=2 sw=2 et tw=80: */
|
|
/* 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";
|
|
|
|
var EXPORTED_SYMBOLS = ["NativeManifests"];
|
|
|
|
const { XPCOMUtils } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/XPCOMUtils.sys.mjs"
|
|
);
|
|
const { AppConstants } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/AppConstants.sys.mjs"
|
|
);
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
|
|
});
|
|
|
|
XPCOMUtils.defineLazyModuleGetters(lazy, {
|
|
OS: "resource://gre/modules/osfile.jsm",
|
|
Schemas: "resource://gre/modules/Schemas.jsm",
|
|
});
|
|
|
|
const DASHED = AppConstants.platform === "linux";
|
|
|
|
// Supported native manifest types, with platform-specific slugs.
|
|
const TYPES = {
|
|
stdio: DASHED ? "native-messaging-hosts" : "NativeMessagingHosts",
|
|
storage: DASHED ? "managed-storage" : "ManagedStorage",
|
|
pkcs11: DASHED ? "pkcs11-modules" : "PKCS11Modules",
|
|
};
|
|
|
|
const NATIVE_MANIFEST_SCHEMA =
|
|
"chrome://extensions/content/schemas/native_manifest.json";
|
|
|
|
const REGPATH = "Software\\Mozilla";
|
|
|
|
var NativeManifests = {
|
|
_initializePromise: null,
|
|
_lookup: null,
|
|
|
|
init() {
|
|
if (!this._initializePromise) {
|
|
let platform = AppConstants.platform;
|
|
if (platform == "win") {
|
|
this._lookup = this._winLookup;
|
|
} else if (platform == "macosx" || platform == "linux") {
|
|
let dirs = [
|
|
Services.dirsvc.get("XREUserNativeManifests", Ci.nsIFile).path,
|
|
Services.dirsvc.get("XRESysNativeManifests", Ci.nsIFile).path,
|
|
];
|
|
this._lookup = (type, name, context) =>
|
|
this._tryPaths(type, name, dirs, context);
|
|
} else {
|
|
throw new Error(
|
|
`Native manifests are not supported on ${AppConstants.platform}`
|
|
);
|
|
}
|
|
this._initializePromise = lazy.Schemas.load(NATIVE_MANIFEST_SCHEMA);
|
|
}
|
|
return this._initializePromise;
|
|
},
|
|
|
|
async _winLookup(type, name, context) {
|
|
const REGISTRY = Ci.nsIWindowsRegKey;
|
|
let regPath = `${REGPATH}\\${TYPES[type]}\\${name}`;
|
|
let path = lazy.WindowsRegistry.readRegKey(
|
|
REGISTRY.ROOT_KEY_CURRENT_USER,
|
|
regPath,
|
|
"",
|
|
REGISTRY.WOW64_64
|
|
);
|
|
if (!path) {
|
|
path = lazy.WindowsRegistry.readRegKey(
|
|
REGISTRY.ROOT_KEY_LOCAL_MACHINE,
|
|
regPath,
|
|
"",
|
|
REGISTRY.WOW64_32
|
|
);
|
|
}
|
|
if (!path) {
|
|
path = lazy.WindowsRegistry.readRegKey(
|
|
REGISTRY.ROOT_KEY_LOCAL_MACHINE,
|
|
regPath,
|
|
"",
|
|
REGISTRY.WOW64_64
|
|
);
|
|
}
|
|
if (!path) {
|
|
return null;
|
|
}
|
|
|
|
let manifest = await this._tryPath(type, path, name, context, true);
|
|
return manifest ? { path, manifest } : null;
|
|
},
|
|
|
|
_tryPath(type, path, name, context, logIfNotFound) {
|
|
return Promise.resolve()
|
|
.then(() => lazy.OS.File.read(path, { encoding: "utf-8" }))
|
|
.then(data => {
|
|
let manifest;
|
|
try {
|
|
manifest = JSON.parse(data);
|
|
} catch (ex) {
|
|
Cu.reportError(
|
|
`Error parsing native manifest ${path}: ${ex.message}`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
let normalized = lazy.Schemas.normalize(
|
|
manifest,
|
|
"manifest.NativeManifest",
|
|
context
|
|
);
|
|
if (normalized.error) {
|
|
Cu.reportError(normalized.error);
|
|
return null;
|
|
}
|
|
manifest = normalized.value;
|
|
|
|
if (manifest.type !== type) {
|
|
Cu.reportError(
|
|
`Native manifest ${path} has type property ${manifest.type} (expected ${type})`
|
|
);
|
|
return null;
|
|
}
|
|
if (manifest.name !== name) {
|
|
Cu.reportError(
|
|
`Native manifest ${path} has name property ${manifest.name} (expected ${name})`
|
|
);
|
|
return null;
|
|
}
|
|
if (
|
|
manifest.allowed_extensions &&
|
|
!manifest.allowed_extensions.includes(context.extension.id)
|
|
) {
|
|
Cu.reportError(
|
|
`This extension does not have permission to use native manifest ${path}`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
return manifest;
|
|
})
|
|
.catch(ex => {
|
|
if (ex instanceof lazy.OS.File.Error && ex.becauseNoSuchFile) {
|
|
if (logIfNotFound) {
|
|
Cu.reportError(
|
|
`Error reading native manifest file ${path}: file is referenced in the registry but does not exist`
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
throw ex;
|
|
});
|
|
},
|
|
|
|
async _tryPaths(type, name, dirs, context) {
|
|
for (let dir of dirs) {
|
|
let path = lazy.OS.Path.join(dir, TYPES[type], `${name}.json`);
|
|
let manifest = await this._tryPath(type, path, name, context, false);
|
|
if (manifest) {
|
|
return { path, manifest };
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Search for a valid native manifest of the given type and name.
|
|
* The directories searched and rules for manifest validation are all
|
|
* detailed in the Native Manifests documentation.
|
|
*
|
|
* @param {string} type The type, one of: "pkcs11", "stdio" or "storage".
|
|
* @param {string} name The name of the manifest to search for.
|
|
* @param {object} context A context object as expected by Schemas.normalize.
|
|
* @returns {object} The contents of the validated manifest, or null if
|
|
* no valid manifest can be found for this type and name.
|
|
*/
|
|
lookupManifest(type, name, context) {
|
|
return this.init().then(() => this._lookup(type, name, context));
|
|
},
|
|
};
|