Bug 1414398 - Read default preferences supplied by add-ons. r=jorgk a=jorgk
new file mode 100644
--- /dev/null
+++ b/common/src/extensionSupport.jsm
@@ -0,0 +1,141 @@
+/* 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/. */
+
+this.EXPORTED_SYMBOLS = [ "extensionDefaults" ];
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+// Cu.import("resource://gre/modules/Deprecated.jsm") - needed for warning.
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+Cu.import("resource:///modules/iteratorUtils.jsm");
+Cu.import("resource:///modules/IOUtils.js");
+
+/**
+ * Reads preferences from addon provided locations (defaults/preferences/*.js)
+ * and stores them in the default preferences branch.
+ */
+function extensionDefaults() {
+
+ function setPref(preferDefault, name, value) {
+ let branch = Services.prefs.getBranch("");
+ if (preferDefault) {
+ let defaultBranch = Services.prefs.getDefaultBranch("");
+ if (defaultBranch.getPrefType(name) == Ci.nsIPrefBranch.PREF_INVALID) {
+ // Only use the default branch if it doesn't already have the pref set.
+ // If there is already a pref with this value on the default branch, the
+ // extension wants to override a built-in value.
+ branch = defaultBranch;
+ } else if (defaultBranch.prefHasUserValue(name)) {
+ // If a pref already has a user-set value it proper type
+ // will be returned (not PREF_INVALID). In that case keep the user's
+ // value and overwrite the default.
+ branch = defaultBranch;
+ }
+ }
+
+ if (typeof value == "boolean") {
+ branch.setBoolPref(name, value);
+ } else if (typeof value == "string") {
+ if (value.startsWith("chrome://") && value.endsWith(".properties")) {
+ let valueLocal = Cc["@mozilla.org/pref-localizedstring;1"]
+ .createInstance(Ci.nsIPrefLocalizedString);
+ valueLocal.data = value;
+ branch.setComplexValue(name, Ci.nsIPrefLocalizedString, valueLocal);
+ } else {
+ branch.setStringPref(name, value);
+ }
+ } else if (typeof value == "number" && Number.isInteger(value)) {
+ branch.setIntPref(name, value);
+ } else if (typeof value == "number" && Number.isFloat(value)) {
+ // Floats are set as char prefs, then retrieved using getFloatPref
+ branch.setCharPref(name, value);
+ }
+ }
+
+ function walkExtensionPrefs(addon) {
+ let foundPrefStrings = [];
+ let prefPath = addon.path;
+ let prefFile = new FileUtils.File(prefPath);
+ if (!prefFile.exists())
+ return [];
+
+ if (prefFile.isDirectory()) {
+ prefFile.append("defaults");
+ prefFile.append("preferences");
+ if (!prefFile.exists() || !prefFile.isDirectory())
+ return [];
+
+ for (let file of fixIterator(prefFile.directoryEntries, Components.interfaces.nsIFile)) {
+ if (file.isFile() && file.leafName.toLowerCase().endsWith(".js")) {
+ foundPrefStrings.push(IOUtils.loadFileToString(file));
+ }
+ }
+ } else if (prefFile.isFile() && prefFile.leafName.endsWith("xpi")) {
+ let zipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]
+ .createInstance(Components.interfaces.nsIZipReader);
+ zipReader.open(prefFile);
+ let entries = zipReader.findEntries("defaults/preferences/*.js");
+
+ while (entries.hasMore()) {
+ let entryName = entries.getNext();
+ let stream = zipReader.getInputStream(entryName);
+ let entrySize = zipReader.getEntry(entryName).realSize;
+ if (entrySize > 0) {
+ let content = NetUtil.readInputStreamToString(stream, entrySize, { charset: "utf-8", replacement: "?" });
+ foundPrefStrings.push(content);
+ }
+ }
+ }
+
+ return foundPrefStrings;
+ }
+
+ function loadAddonPrefs(addon) {
+ let sandbox = new Components.utils.Sandbox(null);
+ sandbox.pref = setPref.bind(undefined, true);
+ sandbox.user_pref = setPref.bind(undefined, false);
+
+ let prefDataStrings = walkExtensionPrefs(addon);
+ for (let prefDataString of prefDataStrings) {
+ try {
+ Components.utils.evalInSandbox(prefDataString, sandbox);
+ } catch (e) {
+ Components.utils.reportError("Error reading default prefs of addon " + addon.defaultLocale.name + ": " + e);
+ }
+ }
+
+ /*
+ TODO: decide whether we need to warn the user/make addon authors to migrate away from these pref files.
+ if (prefDataStrings.length > 0) {
+ Deprecated.warning(addon.defaultLocale.name + " uses defaults/preferences/*.js files to load prefs",
+ "https://bugzilla.mozilla.org/show_bug.cgi?id=1414398");
+ }
+ */
+ }
+
+ let addonsFile = Services.dirsvc.get("ProfDS", Ci.nsIFile);
+ addonsFile.append("extensions.json");
+
+ if (addonsFile.exists() && addonsFile.isFile()) {
+ let fileData = IOUtils.loadFileToString(addonsFile);
+ let addonsData;
+ if (fileData) {
+ try {
+ addonsData = JSON.parse(fileData);
+ } catch (e) {
+ Components.utils.reportError("Parsing of extensions.json failed!");
+ }
+ }
+
+ for (let addon of addonsData.addons) {
+ if (addon.type == "extension" && addon.active && !addon.userDisabled && !addon.appDisabled && !addon.bootstrap)
+ loadAddonPrefs(addon);
+ }
+ }
+}
new file mode 100644
--- /dev/null
+++ b/common/src/moz.build
@@ -0,0 +1,8 @@
+# vim: set filetype=python:
+# 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/.
+
+EXTRA_JS_MODULES += [
+ 'extensionSupport.jsm',
+]
--- a/mail/components/mailGlue.js
+++ b/mail/components/mailGlue.js
@@ -7,32 +7,34 @@ var Ci = Components.interfaces;
var Cc = Components.classes;
var Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource:///modules/distribution.js");
Cu.import("resource:///modules/mailMigrator.js");
+Cu.import("resource:///modules/extensionSupport.jsm");
/**
* Glue code that should be executed before any windows are opened. Any
* window-independent helper methods (a la nsBrowserGlue.js) should go in
* MailUtils.js instead.
*/
function MailGlue() {
XPCOMUtils.defineLazyGetter(this, "_sanitizer",
function() {
let sanitizerScope = {};
Services.scriptloader.loadSubScript("chrome://messenger/content/sanitize.js", sanitizerScope);
return sanitizerScope.Sanitizer;
});
this._init();
+ extensionDefaults(); // extensionSupport.jsm
}
MailGlue.prototype = {
// init (called at app startup)
_init: function MailGlue__init() {
Services.obs.addObserver(this, "xpcom-shutdown", false);
Services.obs.addObserver(this, "final-ui-startup", false);
Services.obs.addObserver(this, "mail-startup-done", false);
--- a/mail/moz.build
+++ b/mail/moz.build
@@ -7,16 +7,17 @@ CONFIGURE_SUBST_FILES += ['installer/Mak
# app is always last as it packages up the built files on mac.
DIRS += [
'base',
'locales',
'extensions',
'themes',
'app',
+ '../common/src',
]
if CONFIG['MAKENSISU']:
DIRS += ['installer/windows']
if CONFIG['MOZ_BUNDLED_FONTS']:
DIRS += ['/%s/browser/fonts' % CONFIG['mozreltopsrcdir']]
--- a/suite/common/src/nsSuiteGlue.js
+++ b/suite/common/src/nsSuiteGlue.js
@@ -6,16 +6,17 @@ const XULNS = "http://www.mozilla.org/ke
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm");
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://gre/modules/LoginManagerParent.jsm");
Components.utils.import("resource:///modules/Sanitizer.jsm");
Components.utils.import("resource:///modules/mailnewsMigrator.js");
+Components.utils.import("resource:///modules/extensionSupport.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
@@ -125,16 +126,17 @@ function onSummaryChanged()
// Constructor
function SuiteGlue() {
XPCOMUtils.defineLazyServiceGetter(this, "_idleService",
"@mozilla.org/widget/idleservice;1",
"nsIIdleService");
this._init();
+ extensionDefaults(); // extensionSupport.jsm
}
SuiteGlue.prototype = {
_saveSession: false,
_sound: null,
_isIdleObserver: false,
_isPlacesDatabaseLocked: false,
_migrationImportsDefaultBookmarks: false,
--- a/suite/moz.build
+++ b/suite/moz.build
@@ -16,22 +16,22 @@ DIRS += [
'modules',
'themes/classic',
'themes/modern',
'profile',
'security',
'shell/public',
'shell/src',
'smile',
+ '../common/src',
]
if CONFIG['MAKENSISU']:
DIRS += ['installer/windows']
if CONFIG['MOZ_BUNDLED_FONTS']:
DIRS += ['/%s/browser/fonts' % CONFIG['mozreltopsrcdir']]
# app is always last as it packages up the built files on mac.
DIRS += [
'build',
'app',
]
-