gecko-dev/toolkit/components/extensions/ExtensionPermissions.jsm

205 строки
5.4 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";
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
ExtensionParent: "resource://gre/modules/ExtensionParent.jsm",
JSONFile: "resource://gre/modules/JSONFile.jsm",
OS: "resource://gre/modules/osfile.jsm",
});
XPCOMUtils.defineLazyGetter(
this,
"StartupCache",
() => ExtensionParent.StartupCache
);
XPCOMUtils.defineLazyGetter(
this,
"Management",
() => ExtensionParent.apiManager
);
var EXPORTED_SYMBOLS = ["ExtensionPermissions"];
const FILE_NAME = "extension-preferences.json";
let prefs;
let _initPromise;
async function _lazyInit() {
let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_NAME);
prefs = new JSONFile({ path });
prefs.data = {};
try {
let { buffer } = await OS.File.read(path);
prefs.data = JSON.parse(new TextDecoder().decode(buffer));
} catch (e) {
if (!e.becauseNoSuchFile) {
Cu.reportError(e);
}
}
}
function lazyInit() {
if (!_initPromise) {
_initPromise = _lazyInit();
}
return _initPromise;
}
function emptyPermissions() {
return { permissions: [], origins: [] };
}
var ExtensionPermissions = {
_update(extensionId, perms) {
prefs.data[extensionId] = perms;
prefs.saveSoon();
return StartupCache.permissions.set(extensionId, perms);
},
async _get(extensionId) {
await lazyInit();
let perms = prefs.data[extensionId];
if (!perms) {
perms = emptyPermissions();
}
return perms;
},
async _getCached(extensionId) {
return StartupCache.permissions.get(extensionId, () =>
this._get(extensionId)
);
},
/**
* Retrieves the optional permissions for the given extension.
* The information may be retrieved from the StartupCache, and otherwise fall
* back to data from the disk (and cache the result in the StartupCache).
*
* @param {string} extensionId The extensionId
* @returns {object} An object with "permissions" and "origins" array.
* The object may be a direct reference to the storage or cache, so its
* value should immediately be used and not be modified by callers.
*/
get(extensionId) {
return this._getCached(extensionId);
},
/**
* Add new permissions for the given extension. `permissions` is
* in the format that is passed to browser.permissions.request().
*
* @param {string} extensionId The extension id
* @param {Object} perms Object with permissions and origins array.
* @param {EventEmitter} emitter optional object implementing emitter interfaces
*/
async add(extensionId, perms, emitter) {
let { permissions, origins } = await this._get(extensionId);
let added = emptyPermissions();
for (let perm of perms.permissions) {
if (!permissions.includes(perm)) {
added.permissions.push(perm);
permissions.push(perm);
}
}
for (let origin of perms.origins) {
origin = new MatchPattern(origin, { ignorePath: true }).pattern;
if (!origins.includes(origin)) {
added.origins.push(origin);
origins.push(origin);
}
}
if (added.permissions.length || added.origins.length) {
await this._update(extensionId, { permissions, origins });
Management.emit("change-permissions", { extensionId, added });
if (emitter) {
emitter.emit("add-permissions", added);
}
}
},
/**
* Revoke permissions from the given extension. `permissions` is
* in the format that is passed to browser.permissions.request().
*
* @param {string} extensionId The extension id
* @param {Object} perms Object with permissions and origins array.
* @param {EventEmitter} emitter optional object implementing emitter interfaces
*/
async remove(extensionId, perms, emitter) {
let { permissions, origins } = await this._get(extensionId);
let removed = emptyPermissions();
for (let perm of perms.permissions) {
let i = permissions.indexOf(perm);
if (i >= 0) {
removed.permissions.push(perm);
permissions.splice(i, 1);
}
}
for (let origin of perms.origins) {
origin = new MatchPattern(origin, { ignorePath: true }).pattern;
let i = origins.indexOf(origin);
if (i >= 0) {
removed.origins.push(origin);
origins.splice(i, 1);
}
}
if (removed.permissions.length || removed.origins.length) {
await this._update(extensionId, { permissions, origins });
Management.emit("change-permissions", { extensionId, removed });
if (emitter) {
emitter.emit("remove-permissions", removed);
}
}
},
async removeAll(extensionId) {
await lazyInit();
StartupCache.permissions.delete(extensionId);
if (prefs.data[extensionId]) {
let removed = prefs.data[extensionId];
delete prefs.data[extensionId];
prefs.saveSoon();
Management.emit("change-permissions", {
extensionId,
removed,
});
}
},
// This is meant for tests only
async _uninit() {
if (!_initPromise) {
return;
}
await _initPromise;
await prefs.finalize();
prefs = null;
_initPromise = null;
},
};