mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-01 00:38:50 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			209 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- mode: js; 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/. */
 | |
| 
 | |
| import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | |
| import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
 | |
| 
 | |
| const lazy = XPCOMUtils.declareLazy({
 | |
|   Schemas: "resource://gre/modules/Schemas.sys.mjs",
 | |
|   WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
 | |
| });
 | |
| 
 | |
| 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";
 | |
| 
 | |
| export 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;
 | |
|     }
 | |
|     if (typeof path !== "string") {
 | |
|       Cu.reportError(
 | |
|         `Native manifest registry entry ${regPath} must be a string path`
 | |
|       );
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     // Normalize in case the extension used / instead of \.
 | |
|     path = path.replaceAll("/", "\\");
 | |
| 
 | |
|     let manifest = await this._tryPath(type, path, name, context, true);
 | |
|     return manifest ? { path, manifest } : null;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Parse a native manifest of the given type and name.
 | |
|    *
 | |
|    * @param {string} type The type, one of: "pkcs11", "stdio" or "storage".
 | |
|    * @param {string} path The path to the manifest file.
 | |
|    * @param {string} name The name of the application.
 | |
|    * @param {object} context A context object as expected by Schemas.normalize.
 | |
|    * @param {object} data The JSON object of the manifest.
 | |
|    * @returns {Promise<object>} The contents of the validated manifest, or null if
 | |
|    *                   the manifest is not valid.
 | |
|    */
 | |
|   async parseManifest(type, path, name, context, data) {
 | |
|     await this.init();
 | |
|     let manifest = data;
 | |
|     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 (
 | |
|       type === "stdio" &&
 | |
|       AppConstants.platform != "win" &&
 | |
|       !PathUtils.isAbsolute(manifest.path)
 | |
|     ) {
 | |
|       // manifest.path is defined for type "stdio" and "pkcs11".
 | |
|       // stdio requires an absolute path on Linux and macOS,
 | |
|       // pkcs11 also accepts relative paths.
 | |
|       Cu.reportError(
 | |
|         `Native manifest ${path} has relative path value ${manifest.path} (expected absolute path)`
 | |
|       );
 | |
|       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;
 | |
|   },
 | |
| 
 | |
|   async _tryPath(type, path, name, context, logIfNotFound) {
 | |
|     let manifest;
 | |
|     try {
 | |
|       manifest = await IOUtils.readJSON(path);
 | |
|     } catch (ex) {
 | |
|       if (ex instanceof SyntaxError && ex.message.startsWith("JSON.parse:")) {
 | |
|         Cu.reportError(`Error parsing native manifest ${path}: ${ex.message}`);
 | |
|         return null;
 | |
|       }
 | |
|       if (DOMException.isInstance(ex) && ex.name == "NotFoundError") {
 | |
|         if (logIfNotFound) {
 | |
|           Cu.reportError(
 | |
|             `Error reading native manifest file ${path}: file is referenced in the registry but does not exist`
 | |
|           );
 | |
|         }
 | |
|         return null;
 | |
|       }
 | |
|       Cu.reportError(ex);
 | |
|       return null;
 | |
|     }
 | |
|     manifest = await this.parseManifest(type, path, name, context, manifest);
 | |
|     return manifest;
 | |
|   },
 | |
| 
 | |
|   async _tryPaths(type, name, dirs, context) {
 | |
|     for (let dir of dirs) {
 | |
|       let path = PathUtils.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));
 | |
|   },
 | |
| };
 | 
