Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,180 @@
/* globals indexedDB */
/**
* This file defines an asynchronous version of the localStorage API, backed by
* an IndexedDB database. It creates a global asyncStorage object that has
* methods like the localStorage object.
*
* To store a value use setItem:
*
* asyncStorage.setItem('key', 'value');
*
* If you want confirmation that the value has been stored, pass a callback
* function as the third argument:
*
* asyncStorage.setItem('key', 'newvalue', function() {
* console.log('new value stored');
* });
*
* To read a value, call getItem(), but note that you must supply a callback
* function that the value will be passed to asynchronously:
*
* asyncStorage.getItem('key', function(value) {
* console.log('The value of key is:', value);
* });
*
* Note that unlike localStorage, asyncStorage does not allow you to store and
* retrieve values by setting and querying properties directly. You cannot just
* write asyncStorage.key; you have to explicitly call setItem() or getItem().
*
* removeItem(), clear(), length(), and key() are like the same-named methods of
* localStorage, but, like getItem() and setItem() they take a callback
* argument.
*
* The asynchronous nature of getItem() makes it tricky to retrieve multiple
* values. But unlike localStorage, asyncStorage does not require the values you
* store to be strings. So if you need to save multiple values and want to
* retrieve them together, in a single asynchronous operation, just group the
* values into a single object. The properties of this object may not include
* DOM elements, but they may include things like Blobs and typed arrays.
*
* Unit tests are in apps/gallery/test/unit/asyncStorage_test.js
*/
this.asyncStorage = (function() {
'use strict';
var DBNAME = 'asyncStorage';
var DBVERSION = 1;
var STORENAME = 'keyvaluepairs';
var db = null;
function withDatabase(f) {
if (db) {
f();
} else {
var openreq = indexedDB.open(DBNAME, DBVERSION);
openreq.onerror = function withStoreOnError() {
console.error('asyncStorage: can\'t open database:',
openreq.error.name);
};
openreq.onupgradeneeded = function withStoreOnUpgradeNeeded() {
// First time setup: create an empty object store
openreq.result.createObjectStore(STORENAME);
};
openreq.onsuccess = function withStoreOnSuccess() {
db = openreq.result;
f();
};
}
}
function withStore(type, callback, oncomplete) {
withDatabase(function() {
var transaction = db.transaction(STORENAME, type);
if (oncomplete) {
transaction.oncomplete = oncomplete;
}
callback(transaction.objectStore(STORENAME));
});
}
function getItem(key, callback) {
var req;
withStore('readonly', function getItemBody(store) {
req = store.get(key);
req.onerror = function getItemOnError() {
console.error('Error in asyncStorage.getItem(): ', req.error.name);
};
}, function onComplete() {
var value = req.result;
if (value === undefined) {
value = null;
}
callback(value);
});
}
function setItem(key, value, callback) {
withStore('readwrite', function setItemBody(store) {
var req = store.put(value, key);
req.onerror = function setItemOnError() {
console.error('Error in asyncStorage.setItem(): ', req.error.name);
};
}, callback);
}
function removeItem(key, callback) {
withStore('readwrite', function removeItemBody(store) {
var req = store.delete(key);
req.onerror = function removeItemOnError() {
console.error('Error in asyncStorage.removeItem(): ', req.error.name);
};
}, callback);
}
function clear(callback) {
withStore('readwrite', function clearBody(store) {
var req = store.clear();
req.onerror = function clearOnError() {
console.error('Error in asyncStorage.clear(): ', req.error.name);
};
}, callback);
}
function length(callback) {
var req;
withStore('readonly', function lengthBody(store) {
req = store.count();
req.onerror = function lengthOnError() {
console.error('Error in asyncStorage.length(): ', req.error.name);
};
}, function onComplete() {
callback(req.result);
});
}
function key(n, callback) {
if (n < 0) {
callback(null);
return;
}
var req;
withStore('readonly', function keyBody(store) {
var advanced = false;
req = store.openCursor();
req.onsuccess = function keyOnSuccess() {
var cursor = req.result;
if (!cursor) {
// this means there weren't enough keys
return;
}
if (n === 0 || advanced) {
// Either 1) we have the first key, return it if that's what they
// wanted, or 2) we've got the nth key.
return;
}
// Otherwise, ask the cursor to skip ahead n records
advanced = true;
cursor.advance(n);
};
req.onerror = function keyOnError() {
console.error('Error in asyncStorage.key(): ', req.error.name);
};
}, function onComplete() {
var cursor = req.result;
callback(cursor ? cursor.key : null);
});
}
return {
getItem: getItem,
setItem: setItem,
removeItem: removeItem,
clear: clear,
length: length,
key: key
};
}());

View file

@ -0,0 +1,35 @@
/* globals dump */
(function() {
'use strict';
function dump_off(msg, optionalObject) {}
function dump_on(msg, optionalObject) {
var output = msg;
if (optionalObject) {
try {
output += JSON.stringify(optionalObject);
} catch(e) {}
}
if (dump) {
var appName = document.location.hostname.replace(/\..*$/, '');
dump('[' + appName + '] ' + output + '\n');
} else {
console.log(output);
}
}
window.DUMP = dump_off; // no traces by default
// enable/disable DUMP according to the related setting
var settings = window.navigator.mozSettings;
var reqGaiaDebug = settings.createLock().get('debug.gaia.enabled');
reqGaiaDebug.onsuccess = function gaiaDebug() {
window.DUMP =
reqGaiaDebug.result['debug.gaia.enabled'] ? dump_on : dump_off;
};
settings.addObserver('debug.gaia.enabled', function(event) {
window.DUMP = event.settingValue ? dump_on : dump_off;
dump_on(event.settingValue ? 'Enabling DUMP' : 'Disabling DUMP');
});
}());

View file

@ -0,0 +1,25 @@
/* exported IAC_API_WAKEUP_REASON_ENABLED_CHANGED */
/* exported IAC_API_WAKEUP_REASON_TRY_DISABLE */
/* exported IAC_API_WAKEUP_REASON_LOGIN */
/* exported IAC_API_WAKEUP_REASON_LOGOUT */
/* exported IAC_API_WAKEUP_REASON_STALE_REGISTRATION */
/* exported IAC_API_WAKEUP_REASON_LOCKSCREEN_CLOSED */
/* exported wakeUpFindMyDevice */
'use strict';
const IAC_API_WAKEUP_REASON_ENABLED_CHANGED = 0;
const IAC_API_WAKEUP_REASON_STALE_REGISTRATION = 1;
const IAC_API_WAKEUP_REASON_LOGIN = 2;
const IAC_API_WAKEUP_REASON_LOGOUT = 3;
const IAC_API_WAKEUP_REASON_TRY_DISABLE = 4;
const IAC_API_WAKEUP_REASON_LOCKSCREEN_CLOSED = 5;
function wakeUpFindMyDevice(reason) {
navigator.mozApps.getSelf().onsuccess = function() {
var app = this.result;
app.connect('findmydevice-wakeup').then(function(ports) {
ports[0].postMessage(reason);
});
};
}

View file

@ -0,0 +1,138 @@
/* exported PasscodeHelper */
/* globals crypto, TextEncoder */
(function(exports) {
'use strict';
const SET_DIGEST_VALUE = 'lockscreen.passcode-lock.digest.value';
const SET_DIGEST_SALT = 'lockscreen.passcode-lock.digest.salt';
const SET_DIGEST_ITERATIONS = 'lockscreen.passcode-lock.digest.iterations';
const SET_DIGEST_ALGORITHM = 'lockscreen.passcode-lock.digest.algorithm';
const DEFAULT_ALGORITHM = 'SHA-1'; //XXX Update to SHA-256 after bug 554827?
const DEFAULT_ITERATIONS = 1000;
const PBKDF2_OUTPUT_LENGTH = 160; //XXX HMAC-SHA-1 = 160. Change for SHA-256!
function _encode(str) {
return new TextEncoder('utf-8').encode(str);
}
function _toTypedArray(obj) {
// SettingsAPI doesnt like arrays and gives us an Object { 1: .., 2: .. }
var a = [];
for (var key of Object.keys(obj)) {
a.push(obj[key]);
}
return new Uint8Array(a);
}
function _deriveBits(pwKey, salt, iterations, algorithm) {
var params = {
name: 'PBKDF2',
hash: algorithm,
salt: salt,
iterations: iterations
};
return crypto.subtle.deriveBits(params, pwKey, PBKDF2_OUTPUT_LENGTH);
}
function _makeDigest(pass, salt, iterations, algorithm) {
var bytes = _encode(pass);
var promise = crypto.subtle.importKey('raw', bytes, 'PBKDF2', false,
['deriveBits']);
promise = promise.then((pwKey) => {
return _deriveBits(pwKey, salt, iterations, algorithm);
});
promise = promise.catch((error) => {
console.error('PasscodeHelper: _derive_bits() failed!', error);
throw error;
});
return promise;
}
function setPasscode(newPass) {
// Always generate a new salt.
var salt = crypto.getRandomValues(new Uint8Array(8));
/* the combined lock.get() makes it quite ugly. we get
an Array of objects, each with just one key/value,
which is the requested setting. let's destruct as follows:
*/
var promise = _makeDigest(newPass, salt, DEFAULT_ITERATIONS,
DEFAULT_ALGORITHM);
promise = promise.then((digest) => {
return _storeNewPasscode(digest, salt);
});
promise = promise.catch((error) => {
console.error('PasscodeHelper: Could not make digest:', error);
throw error;
});
return promise;
}
function _storeNewPasscode(digest, salt) {
// Note: We can now store the salt, since digest was generated!
var digestUint8 = new Uint8Array(digest);
var newSettings = {
[SET_DIGEST_SALT]: salt,
[SET_DIGEST_VALUE]: digestUint8,
[SET_DIGEST_ITERATIONS]: DEFAULT_ITERATIONS,
[SET_DIGEST_ALGORITHM]: DEFAULT_ALGORITHM,
};
var lock = navigator.mozSettings.createLock();
var req = lock.set(newSettings);
return req.then(() => {
return digestUint8;
}, (error) => {
console.error('PasscodeHelper: Couldnt store new digest', error);
throw error;
});
}
function checkPasscode(testPass) {
//get salt & digest out of settings
var lock = navigator.mozSettings.createLock();
var storedParams = Promise.all([
lock.get(SET_DIGEST_SALT),
lock.get(SET_DIGEST_ITERATIONS),
lock.get(SET_DIGEST_ALGORITHM),
lock.get(SET_DIGEST_VALUE)
]);
return storedParams.then((values) => {
return _makeAndCompare(...values, testPass);
})
.catch((error) => {
console.error('PasscodeHelper: Couldnt get digest Settings:', error);
throw error;
});
}
function _makeAndCompare(salt, iterations, algorithm, storedDigest,
testPass) {
/* the combined lock.get() makes it quite ugly. we get
an Array of objects, each with just one key/value,
which is the requested setting. let's destruct as follows:
*/
var _salt = _toTypedArray(salt[SET_DIGEST_SALT]);
var _iterations = iterations[SET_DIGEST_ITERATIONS];
var _algorithm = algorithm[SET_DIGEST_ALGORITHM];
var _storedDigest = _toTypedArray(storedDigest[SET_DIGEST_VALUE]);
var promise = _makeDigest(testPass, _salt, _iterations, _algorithm);
promise = promise.then((digest) => {
var typedDigest = new Uint8Array(digest);
return _compareDigests(_storedDigest, typedDigest);
});
promise = promise.catch((error) => {
console.error('PasscodeHelper: Couldnt create digest', error);
throw error;
});
return promise;
}
function _compareDigests(buf1, buf2) {
if (buf1.byteLength !== buf2.byteLength) {
return false;
}
for (var i = 0; i < buf1.byteLength; i++) {
if (buf1[i] !== buf2[i]) {
return false;
}
}
return true;
}
exports.PasscodeHelper = {
set: setPasscode,
check: checkPasscode
};
})(this);

View file

@ -0,0 +1,125 @@
(function(exports) {
'use strict';
/**
* SettingsHelper simplifies mozSettings access. It provides getter and setter
* a specified setting. It iscreated by passing a setting key and an
* optional default value.
*
* @param {String} key - The setting key
* @param {Object} defaultValue - The default value
*
* Example:
* // create a helper with a default false
* var voicePrivacyHelper = SettingsHelper('ril.voicePrivacy.enabled', false);
* // get value
* voicePrivacyHelper.get(function(value) {});
* // set value
* voicePrivacyHelper.set(false, function() {});
*/
var SettingsHelper = function(key, defaultValue) {
var SETTINGS_KEY = key;
var _settings = navigator.mozSettings;
var _value = null;
var _defaultValue = defaultValue;
var _isReady = false;
var _callbacks = [];
var _return = function sh_return(callback) {
if (!callback) {
return;
}
callback.apply(null, Array.prototype.slice.call(arguments, 1));
};
var _ready = function sh_ready(callback) {
if (!callback) {
return;
}
if (_isReady) {
callback();
} else {
_callbacks.push(callback);
}
};
var _getValue = function sh_getValue(callback) {
var req = _settings.createLock().get(SETTINGS_KEY);
req.onsuccess = function() {
_return(callback, req.result[SETTINGS_KEY]);
};
req.onerror = function() {
console.error('Error getting ' + SETTINGS_KEY + '.');
_return(callback, null);
};
};
var _setValue = function sh_setValue(value, callback) {
var obj = {};
obj[SETTINGS_KEY] = value;
var req = _settings.createLock().set(obj);
req.onsuccess = function() {
_return(callback);
};
req.onerror = function() {
console.error('Error setting ' + SETTINGS_KEY + '.');
_return(callback);
};
};
var _valuechanged = function sh_valuehanged(e) {
_value = e.settingValue;
};
var _init = function sh_init(callback) {
_getValue(function(value) {
_value = value ? value : _defaultValue;
_return(callback);
});
_settings.addObserver(SETTINGS_KEY, _valuechanged);
};
_init(function() {
_isReady = true;
_callbacks.forEach(function(callback) {
callback();
});
});
return {
/**
* Get the setting value.
*
* @param {Function} callback - The setting value will be passed in the
callback function.
*/
get: function(callback) {
_ready(function() {
_return(callback, _value ? _value : _defaultValue);
});
},
/**
* Set the setting value.
*
* @param {Object} value The setting value
* @param {Function} callback The callback function.
*/
set: function(value, callback) {
_ready(function() {
_setValue(value, _return.bind(null, callback));
});
},
/**
* Cleanup ressources, specifically observers.
*/
uninit: function() {
_settings.removeObserver(SETTINGS_KEY, _valuechanged);
}
};
};
exports.SettingsHelper = SettingsHelper;
})(this);

View file

@ -0,0 +1,76 @@
/* exported SettingsListener */
'use strict';
var SettingsListener = {
/* lock stores here */
_lock: null,
/* keep record of observers in order to remove them in the future */
_observers: [],
/**
* getSettingsLock: create a lock or retrieve one that we saved.
* mozSettings.createLock() is expensive and lock should be reused
* whenever possible.
*/
getSettingsLock: function sl_getSettingsLock() {
// If there is a lock present we return that
if (this._lock && !this._lock.closed) {
return this._lock;
}
// If there isn't we return one.
var settings = window.navigator.mozSettings;
return (this._lock = settings.createLock());
},
observe: function sl_observe(name, defaultValue, callback) {
var settings = window.navigator.mozSettings;
if (!settings) {
window.setTimeout(function() { callback(defaultValue); });
return;
}
var req;
try {
req = this.getSettingsLock().get(name);
} catch (e) {
// It is possible (but rare) for getSettingsLock() to return
// a SettingsLock object that is no longer valid.
// Until https://bugzilla.mozilla.org/show_bug.cgi?id=793239
// is fixed, we just catch the resulting exception and try
// again with a fresh lock
console.warn('Stale lock in settings_listener.js.',
'See https://bugzilla.mozilla.org/show_bug.cgi?id=793239');
this._lock = null;
req = this.getSettingsLock().get(name);
}
req.addEventListener('success', (function onsuccess() {
callback(typeof(req.result[name]) != 'undefined' ?
req.result[name] : defaultValue);
}));
var settingChanged = function settingChanged(evt) {
callback(evt.settingValue);
};
settings.addObserver(name, settingChanged);
this._observers.push({
name: name,
callback: callback,
observer: settingChanged
});
},
unobserve: function sl_unobserve(name, callback) {
var settings = window.navigator.mozSettings;
var that = this;
this._observers.forEach(function(value, index) {
if (value.name === name && value.callback === callback) {
settings.removeObserver(name, value.observer);
that._observers.splice(index, 1);
}
});
}
};

View file

@ -0,0 +1,33 @@
/* exported SettingsURL */
'use strict';
// This library helps with managing settings that can either send back
// string uris or blobs that need to be turned into blob: uris.
// Usage is as follows:
// var blobURL = new SettingsURL();
// element.src = blobURL.set(settingValue);
function SettingsURL() {
this._url = null;
this._isBlob = false;
}
SettingsURL.prototype = {
set: function(value) {
if (this._isBlob) {
window.URL.revokeObjectURL(this._url);
}
if (value instanceof Blob) {
this._isBlob = true;
this._url = window.URL.createObjectURL(value);
} else {
this._isBlob = false;
this._url = value;
}
return this._url;
},
get: function() {
return this._url;
}
};