123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- /* 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 Cc = Components.classes;
- var Ci = Components.interfaces;
- var Cu = Components.utils;
- Cu.importGlobalProperties(['Blob', 'File']);
- Cu.import("resource://gre/modules/Services.jsm");
- this.EXPORTED_SYMBOLS = ["SettingsDB", "SETTINGSDB_NAME", "SETTINGSSTORE_NAME"];
- var DEBUG = false;
- var VERBOSE = false;
- try {
- DEBUG =
- Services.prefs.getBoolPref("dom.mozSettings.SettingsDB.debug.enabled");
- VERBOSE =
- Services.prefs.getBoolPref("dom.mozSettings.SettingsDB.verbose.enabled");
- } catch (ex) { }
- function debug(s) {
- dump("-*- SettingsDB: " + s + "\n");
- }
- const TYPED_ARRAY_THINGS = new Set([
- "Int8Array",
- "Uint8Array",
- "Uint8ClampedArray",
- "Int16Array",
- "Uint16Array",
- "Int32Array",
- "Uint32Array",
- "Float32Array",
- "Float64Array",
- ]);
- this.SETTINGSDB_NAME = "settings";
- this.SETTINGSDB_VERSION = 8;
- this.SETTINGSSTORE_NAME = "settings";
- Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
- Cu.import("resource://gre/modules/FileUtils.jsm");
- Cu.import("resource://gre/modules/NetUtil.jsm");
- this.SettingsDB = function SettingsDB() {}
- SettingsDB.prototype = {
- __proto__: IndexedDBHelper.prototype,
- upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
- let objectStore;
- if (aOldVersion == 0) {
- objectStore = aDb.createObjectStore(SETTINGSSTORE_NAME, { keyPath: "settingName" });
- if (VERBOSE) debug("Created object stores");
- } else if (aOldVersion == 1) {
- if (VERBOSE) debug("Get object store for upgrade and remove old index");
- objectStore = aTransaction.objectStore(SETTINGSSTORE_NAME);
- objectStore.deleteIndex("settingValue");
- } else {
- if (VERBOSE) debug("Get object store for upgrade");
- objectStore = aTransaction.objectStore(SETTINGSSTORE_NAME);
- }
- // Loading resource://app/defaults/settings.json doesn't work because
- // settings.json is not in the omnijar.
- // So we look for the app dir instead and go from here...
- let settingsFile = FileUtils.getFile("DefRt", ["settings.json"], false);
- if (!settingsFile || (settingsFile && !settingsFile.exists())) {
- // On b2g desktop builds the settings.json file is moved in the
- // profile directory by the build system.
- settingsFile = FileUtils.getFile("ProfD", ["settings.json"], false);
- if (!settingsFile || (settingsFile && !settingsFile.exists())) {
- return;
- }
- }
- let chan = NetUtil.newChannel({
- uri: NetUtil.newURI(settingsFile),
- loadUsingSystemPrincipal: true});
- let stream = chan.open2();
- // Obtain a converter to read from a UTF-8 encoded input stream.
- let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
- .createInstance(Ci.nsIScriptableUnicodeConverter);
- converter.charset = "UTF-8";
- let rawstr = converter.ConvertToUnicode(NetUtil.readInputStreamToString(
- stream,
- stream.available()) || "");
- let settings;
- try {
- settings = JSON.parse(rawstr);
- } catch(e) {
- if (DEBUG) debug("Error parsing " + settingsFile.path + " : " + e);
- return;
- }
- stream.close();
- objectStore.openCursor().onsuccess = function(event) {
- let cursor = event.target.result;
- if (cursor) {
- let value = cursor.value;
- if (value.settingName in settings) {
- if (VERBOSE) debug("Upgrade " +settings[value.settingName]);
- value.defaultValue = this.prepareValue(settings[value.settingName]);
- delete settings[value.settingName];
- if ("settingValue" in value) {
- value.userValue = this.prepareValue(value.settingValue);
- delete value.settingValue;
- }
- cursor.update(value);
- } else if ("userValue" in value || "settingValue" in value) {
- value.defaultValue = undefined;
- if (aOldVersion == 1 && value.settingValue) {
- value.userValue = this.prepareValue(value.settingValue);
- delete value.settingValue;
- }
- cursor.update(value);
- } else {
- cursor.delete();
- }
- cursor.continue();
- } else {
- for (let name in settings) {
- let value = this.prepareValue(settings[name]);
- if (VERBOSE) debug("Set new:" + name +", " + value);
- objectStore.add({ settingName: name, defaultValue: value, userValue: undefined });
- }
- }
- }.bind(this);
- },
- // If the value is a data: uri, convert it to a Blob.
- convertDataURIToBlob: function(aValue) {
- /* base64 to ArrayBuffer decoding, from
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
- */
- function b64ToUint6 (nChr) {
- return nChr > 64 && nChr < 91 ?
- nChr - 65
- : nChr > 96 && nChr < 123 ?
- nChr - 71
- : nChr > 47 && nChr < 58 ?
- nChr + 4
- : nChr === 43 ?
- 62
- : nChr === 47 ?
- 63
- :
- 0;
- }
- function base64DecToArr(sBase64, nBlocksSize) {
- let sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""),
- nInLen = sB64Enc.length,
- nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize
- : nInLen * 3 + 1 >> 2,
- taBytes = new Uint8Array(nOutLen);
- for (let nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
- nMod4 = nInIdx & 3;
- nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
- if (nMod4 === 3 || nInLen - nInIdx === 1) {
- for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
- taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
- }
- nUint24 = 0;
- }
- }
- return taBytes;
- }
- // Check if we have a data: uri, and if it's base64 encoded.
- // data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...
- if (typeof aValue == "string" && aValue.startsWith("data:")) {
- try {
- let uri = Services.io.newURI(aValue, null, null);
- // XXX: that would be nice to reuse the c++ bits of the data:
- // protocol handler instead.
- let mimeType = "application/octet-stream";
- let mimeDelim = aValue.indexOf(";");
- if (mimeDelim !== -1) {
- mimeType = aValue.substring(5, mimeDelim);
- }
- let start = aValue.indexOf(",") + 1;
- let isBase64 = ((aValue.indexOf("base64") + 7) == start);
- let payload = aValue.substring(start);
- return new Blob([isBase64 ? base64DecToArr(payload) : payload],
- { type: mimeType });
- } catch(e) {
- dump(e);
- }
- }
- return aValue
- },
- getObjectKind: function(aObject) {
- if (aObject === null || aObject === undefined) {
- return "primitive";
- } else if (Array.isArray(aObject)) {
- return "array";
- } else if (aObject instanceof File) {
- return "file";
- } else if (aObject instanceof Ci.nsIDOMBlob) {
- return "blob";
- } else if (aObject.constructor.name == "Date") {
- return "date";
- } else if (TYPED_ARRAY_THINGS.has(aObject.constructor.name)) {
- return aObject.constructor.name;
- } else if (typeof aObject == "object") {
- return "object";
- } else {
- return "primitive";
- }
- },
- // Makes sure any property that is a data: uri gets converted to a Blob.
- prepareValue: function(aObject) {
- let kind = this.getObjectKind(aObject);
- if (kind == "array") {
- let res = [];
- aObject.forEach(function(aObj) {
- res.push(this.prepareValue(aObj));
- }, this);
- return res;
- } else if (kind == "file" || kind == "blob" || kind == "date") {
- return aObject;
- } else if (kind == "primitive") {
- return this.convertDataURIToBlob(aObject);
- }
- // Fall-through, we now have a dictionary object.
- let res = {};
- for (let prop in aObject) {
- res[prop] = this.prepareValue(aObject[prop]);
- }
- return res;
- },
- init: function init() {
- this.initDBHelper(SETTINGSDB_NAME, SETTINGSDB_VERSION,
- [SETTINGSSTORE_NAME]);
- }
- }
|