fune/devtools/shared/gcli/source/lib/gcli/settings.js

275 lines
7.4 KiB
JavaScript

/*
* Copyright 2012, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var imports = {};
var Cc = require('chrome').Cc;
var Ci = require('chrome').Ci;
var Cu = require('chrome').Cu;
var XPCOMUtils = require('resource://gre/modules/XPCOMUtils.jsm').XPCOMUtils;
var Services = require("Services");
XPCOMUtils.defineLazyGetter(imports, 'prefBranch', function() {
var prefService = Cc['@mozilla.org/preferences-service;1']
.getService(Ci.nsIPrefService);
return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch);
});
var util = require('./util/util');
/**
* All local settings have this prefix when used in Firefox
*/
var DEVTOOLS_PREFIX = 'devtools.gcli.';
/**
* A manager for the registered Settings
*/
function Settings(types, settingValues) {
this._types = types;
if (settingValues != null) {
throw new Error('settingValues is not supported when writing to prefs');
}
// Collection of preferences for sorted access
this._settingsAll = [];
// Collection of preferences for fast indexed access
this._settingsMap = new Map();
// Flag so we know if we've read the system preferences
this._hasReadSystem = false;
// Event for use to detect when the list of settings changes
this.onChange = util.createEvent('Settings.onChange');
}
/**
* Load system prefs if they've not been loaded already
* @return true
*/
Settings.prototype._readSystem = function() {
if (this._hasReadSystem) {
return;
}
imports.prefBranch.getChildList('').forEach(name => {
var setting = new Setting(this, name);
this._settingsAll.push(setting);
this._settingsMap.set(name, setting);
});
this._settingsAll.sort((s1, s2) => {
return s1.name.localeCompare(s2.name);
});
this._hasReadSystem = true;
};
/**
* Get an array containing all known Settings filtered to match the given
* filter (string) at any point in the name of the setting
*/
Settings.prototype.getAll = function(filter) {
this._readSystem();
if (filter == null) {
return this._settingsAll;
}
return this._settingsAll.filter(setting => {
return setting.name.includes(filter);
});
};
/**
* Add a new setting
*/
Settings.prototype.add = function(prefSpec) {
var setting = new Setting(this, prefSpec);
if (this._settingsMap.has(setting.name)) {
// Once exists already, we're going to need to replace it in the array
for (var i = 0; i < this._settingsAll.length; i++) {
if (this._settingsAll[i].name === setting.name) {
this._settingsAll[i] = setting;
}
}
}
this._settingsMap.set(setting.name, setting);
this.onChange({ added: setting.name });
return setting;
};
/**
* Getter for an existing setting. Generally use of this function should be
* avoided. Systems that define a setting should export it if they wish it to
* be available to the outside, or not otherwise. Use of this function breaks
* that boundary and also hides dependencies. Acceptable uses include testing
* and embedded uses of GCLI that pre-define all settings (e.g. Firefox)
* @param name The name of the setting to fetch
* @return The found Setting object, or undefined if the setting was not found
*/
Settings.prototype.get = function(name) {
// We might be able to give the answer without needing to read all system
// settings if this is an internal setting
var found = this._settingsMap.get(name);
if (!found) {
found = this._settingsMap.get(DEVTOOLS_PREFIX + name);
}
if (found) {
return found;
}
if (this._hasReadSystem) {
return undefined;
}
else {
this._readSystem();
found = this._settingsMap.get(name);
if (!found) {
found = this._settingsMap.get(DEVTOOLS_PREFIX + name);
}
return found;
}
};
/**
* Remove a setting. A no-op in this case
*/
Settings.prototype.remove = function() {
};
exports.Settings = Settings;
/**
* A class to wrap up the properties of a Setting.
* @see toolkit/components/viewconfig/content/config.js
*/
function Setting(settings, prefSpec) {
this._settings = settings;
if (typeof prefSpec === 'string') {
// We're coming from getAll() i.e. a full listing of prefs
this.name = prefSpec;
this.description = '';
}
else {
// A specific addition by GCLI
this.name = DEVTOOLS_PREFIX + prefSpec.name;
if (prefSpec.ignoreTypeDifference !== true && prefSpec.type) {
if (this.type.name !== prefSpec.type) {
throw new Error('Locally declared type (' + prefSpec.type + ') != ' +
'Mozilla declared type (' + this.type.name + ') for ' + this.name);
}
}
this.description = prefSpec.description;
}
this.onChange = util.createEvent('Setting.onChange');
}
/**
* Reset this setting to it's initial default value
*/
Setting.prototype.setDefault = function() {
imports.prefBranch.clearUserPref(this.name);
Services.prefs.savePrefFile(null);
};
/**
* What type is this property: boolean/integer/string?
*/
Object.defineProperty(Setting.prototype, 'type', {
get: function() {
switch (imports.prefBranch.getPrefType(this.name)) {
case imports.prefBranch.PREF_BOOL:
return this._settings._types.createType('boolean');
case imports.prefBranch.PREF_INT:
return this._settings._types.createType('number');
case imports.prefBranch.PREF_STRING:
return this._settings._types.createType('string');
default:
throw new Error('Unknown type for ' + this.name);
}
},
enumerable: true
});
/**
* What type is this property: boolean/integer/string?
*/
Object.defineProperty(Setting.prototype, 'value', {
get: function() {
switch (imports.prefBranch.getPrefType(this.name)) {
case imports.prefBranch.PREF_BOOL:
return imports.prefBranch.getBoolPref(this.name);
case imports.prefBranch.PREF_INT:
return imports.prefBranch.getIntPref(this.name);
case imports.prefBranch.PREF_STRING:
var value = imports.prefBranch.getStringPref(this.name);
// In case of a localized string
if (/^chrome:\/\/.+\/locale\/.+\.properties/.test(value)) {
value = imports.prefBranch.getComplexValue(this.name,
Ci.nsIPrefLocalizedString).data;
}
return value;
default:
throw new Error('Invalid value for ' + this.name);
}
},
set: function(value) {
if (imports.prefBranch.prefIsLocked(this.name)) {
throw new Error('Locked preference ' + this.name);
}
switch (imports.prefBranch.getPrefType(this.name)) {
case imports.prefBranch.PREF_BOOL:
imports.prefBranch.setBoolPref(this.name, value);
break;
case imports.prefBranch.PREF_INT:
imports.prefBranch.setIntPref(this.name, value);
break;
case imports.prefBranch.PREF_STRING:
imports.prefBranch.setStringPref(this.name, value);
break;
default:
throw new Error('Invalid value for ' + this.name);
}
Services.prefs.savePrefFile(null);
},
enumerable: true
});