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,75 @@
'use strict';
(function(exports) {
var URI = ['', 'http://www.', 'https://www.', 'http://', 'https://', 'tel:',
'mailto:', 'ftp://anonymous:anonymous@', 'ftp://ftp.', 'ftps://', 'sftp://',
'smb://', 'nfs://', 'ftp://', 'dav://', 'news:', 'telnet://', 'imap:',
'rtsp://', 'urn:', 'pop:', 'sip:', 'sips:', 'tftp:', 'btspp://',
'btl2cap://', 'btgoep://', 'tcpobex://', 'irdaobex://', 'file://',
'urn:epc:id:', 'urn:epc:tag:', 'urn:epc:pat:', 'urn:epc:raw:', 'urn:epc:',
'urn:nfc:'];
function NDEFHelper() { }
NDEFHelper.prototype = {
/**
* Create MozNDEFRecord whose content is the uri.
*/
createURI: function nh_createURI(uri) {
var id = 0;
// start with 1 since index 0 is ''.
for (var i = 1; i < URI.length; i++) {
if (uri.startsWith(URI[i])) {
id = i;
}
}
uri = String.fromCharCode(id) + uri.substring(URI[id].length);
var enc = new TextEncoder('utf-8');
return new MozNDEFRecord({tnf: 'well-known',
type: enc.encode('U'),
payload: enc.encode(uri)});
},
parseURI: function nh_parseURI(ndef) {
if (!ndef || ndef.tnf != 'well-known' || ndef.type[0] != 0x55) {
return null;
}
var payload = ndef.payload;
var prefix = URI[payload[0]];
if (!prefix) {
return null;
}
var dec = new TextDecoder('utf-8');
return prefix + dec.decode(payload.subarray(1));
},
/**
* Utils to dump Uint8Array.
*/
dumpUint8Array: function nh_dumpUint8Array(array) {
if (!array) {
return 'null';
}
var str = '[';
var i;
var arrayLen = array ? array.length : 0;
for (i = 0; i < arrayLen; i++) {
str += '0x' + array[i].toString(16);
if (i != array.length - 1) {
str += ', ';
}
}
return str + ']';
}
};
exports.NDEFHelper = NDEFHelper;
})(window);

View file

@ -0,0 +1,60 @@
var NfcDispatchDemo = {
tagContainer: null,
init: function nd_init() {
dump('NfcDispatchDemo init');
var content = document.getElementById('content');
var globalMsg = document.getElementById('global-message');
this.tagContainer = content.querySelector('[data-type="tag-container"]')
var nfc = window.navigator.mozNfc;
if (!nfc) {
globalMsg.textContent = 'NFC API not available.';
return;
}
if (!nfc.enabled) {
globalMsg.textContent = 'NFC is not enabled.';
return;
}
document.getElementById('global-message').textContent = '';
nfc.ontagfound = this.handleTagFound.bind(this);
nfc.onpeerfound = this.handlePeerFound.bind(this);
},
handleTagFound: function nd_handleTagFound(event) {
var ndefRecords = event.ndefRecords;
var tagUri =
this.tagContainer.querySelector('[data-type="tag-uri"]');
tagUri.textContent = '';
var result =
this.tagContainer.querySelector('[data-type="dispatch-result"]');
var ndefHelper = new NDEFHelper();
var ndef = Array.isArray(ndefRecords) && ndefRecords[0];
var uri = ndefHelper.parseURI(ndef);
tagUri.textContent = uri;
if (uri.startsWith('https://www.mozilla.org')) {
result.textContent = 'Default Prevented';
return false;
} else {
result.textContent = 'Default Action Executed';
return true;
}
},
handlePeerFound: function nd_handlePeerFound(event) {
var tagUri =
this.tagContainer.querySelector('[data-type="tag-uri"]');
tagUri.textContent = '';
var result =
this.tagContainer.querySelector('[data-type="dispatch-result"]');
result.textContent = 'Default Prevented';
event.preventDefault();
}
};
window.addEventListener('load', () => NfcDispatchDemo.init());

View file

@ -0,0 +1,47 @@
var NfcFormatDemo = {
tagContainer: null,
init: function nd_init() {
dump('NfcFormatDemo init');
var content = document.getElementById('content');
var globalMsg = document.getElementById('global-message');
this.tagContainer = content.querySelector('[data-type="tag-container"]')
var nfc = window.navigator.mozNfc;
if (!nfc) {
globalMsg.textContent = 'NFC API not available.';
return;
}
if (!nfc.enabled) {
globalMsg.textContent = 'NFC is not enabled.';
return;
}
document.getElementById('global-message').textContent = '';
nfc.ontagfound = this.handleTagFound.bind(this);
},
handleTagFound: function nd_handleTagFound(event) {
var tag = event.tag;
var formatable =
this.tagContainer.querySelector('[data-type="formatable"]');
formatable.textContent = tag.isFormatable != null ?
tag.isFormatable : 'null';
var result = this.tagContainer.querySelector('[data-type="format-result"]');
tag.format().then(() => {
result.style.color = "Green";
result.textContent = "Pass";
}).catch((err) => {
result.style.color = "Red";
result.textContent = err;
});
return false;
}
};
window.addEventListener('load', () => NfcFormatDemo.init());

View file

@ -0,0 +1,173 @@
var NfcReadDemo = {
tagContainer: null,
ndefMsgContainer: null,
tag: null,
init: function nd_init() {
dump('NfcReadDemo init');
var content = document.getElementById('content');
var errorMsg = document.getElementById('error-message');
this.tagContainer = document.getElementById('tag-container');
this.ndefMsgContainer = document.getElementById('ndef-msg-container');
this.ndefMsgContainer.hidden = true;
var nfc = window.navigator.mozNfc;
if (!nfc) {
errorMsg.textContent = 'NFC API not available.';
return;
}
if (!nfc.enabled) {
errorMsg.textContent = 'NFC is not enabled.';
return;
}
document.getElementById('error-message').textContent = '';
nfc.ontagfound = this.handleTagFound.bind(this);
nfc.ontaglost = this.handleTagLost.bind(this);
},
handleTagFound: function nd_handleTagFound(event) {
// clear success/error message.
var errorMsg = document.getElementById('error-message');
errorMsg.textContent = '';
var sucMsg = document.getElementById('success-message');
sucMsg.textContent = '';
this.tag = event.tag;
var tag = event.tag;
var techList = this.tagContainer.querySelector('[data-type="tech-list"]');
var tagId = this.tagContainer.querySelector('[data-type="tag-id"]');
var tagType = this.tagContainer.querySelector('[data-type="tag-type"]');
var maxNDEFSize = this.tagContainer.
querySelector('[data-type="max-ndef-size"]');
var readOnly = this.tagContainer.querySelector('[data-type="read-only"]');
var formatable = this.tagContainer.
querySelector('[data-type="formatable"]');
var canReadOnly =
this.tagContainer.querySelector('[data-type="can-be-made-read-only"]');
var isLost = this.tagContainer.querySelector('[data-type="is-lost"]');
var ndefHelper = new NDEFHelper();
techList.textContent = tag.techList || 'null';
tagId.textContent = ndefHelper.dumpUint8Array(tag.id);
tagType.textContent = tag.type || 'null';
maxNDEFSize.textContent = tag.maxNDEFSize || 'null';
readOnly.textContent = tag.isReadOnly != null ?
tag.isReadOnly : 'null';
formatable.textContent = tag.isFormatable != null ?
tag.isFormatable : 'null';
canReadOnly.textContent = tag.canBeMadeReadOnly != null ?
tag.canBeMadeReadOnly : 'null';
isLost.textContent = tag.isLost;
var ndefRecords = event.ndefRecords;
var ndefLen = ndefRecords ? ndefRecords.length : 0;
// clear previous ndef information
var recordCount = this.ndefMsgContainer.
querySelector('[data-type="record-count"]');
var previousCount = recordCount.textContent;
var i;
for (i = 0; i < previousCount; i++) {
var ndefContainer = document.getElementById("ndef#" + i);
if (ndefContainer) {
this.ndefMsgContainer.removeChild(ndefContainer);
}
}
// if no NDEF Records are contained, bail out.
if (!ndefLen) {
this.ndefMsgContainer.hidden = true;
return true;
}
this.showNDEFRecords(event.ndefRecords);
tag.readNDEF().then(
function (ndefRecords) {
this.compareRecords(ndefRecords, event.ndefRecords);
}.bind(this),
error => {
errorMsg.textContent = 'readNDEF failed, ' + error;
});
return false;
},
handleTagLost: function nd_handleTagLost() {
// update tag.isLost field.
var tag = this.tag;
var isLost = this.tagContainer.querySelector('[data-type="is-lost"]');
isLost.textContent = tag.isLost;
},
compareRecords: function nd_compareRecords(recordsA, recordsB) {
var errorMsg = document.getElementById('error-message');
if (recordsA.length != recordsB.length) {
errorMsg.textContent = 'Number of Records are different.';
return;
}
var identical = true;
for (var i = 0; i < recordsA.length; i++) {
var A = recordsA[i];
var B = recordsB[i];
var props = ['tnf', 'type', 'id', 'payload'];
for (var prop of props) {
if (A.prop != B.prop) {
identical = false;
errorMsg.textContent = prop + ' mismatched in the ' + i + 'th record';
break;
}
}
}
if (identical) {
var sucMsg = document.getElementById('success-message');
sucMsg.textContent = 'readNDEF succeeds.';
}
},
showNDEFRecords: function nd_showNDEFRecords(ndefRecords) {
var ndefLen = ndefRecords.length;
var recordCount = this.ndefMsgContainer.
querySelector('[data-type="record-count"]');
recordCount.textContent = ndefLen;
var ndefTemplate = document.getElementById("ndef-template");
ndefTemplate.hidden = true;
var ndefHelper = new NDEFHelper();
for (i = 0; i < ndefLen; i++) {
var ndefContainer = document.getElementById("ndef#" + i);
if (!ndefContainer) {
ndefContainer = ndefTemplate.cloneNode(true);
ndefContainer.id = 'ndef#' + i;
var tnf = ndefContainer.querySelector('[data-type="ndef-tnf"]');
tnf.textContent = ndefRecords[i].tnf;
var type = ndefContainer.querySelector('[data-type="ndef-type"]');
type.textContent = ndefHelper.dumpUint8Array(ndefRecords[i].type);
var id = ndefContainer.querySelector('[data-type="ndef-id"]');
id.textContent = ndefHelper.dumpUint8Array(ndefRecords[i].id);
var payload = ndefContainer.querySelector('[data-type="ndef-payload"]');
payload.textContent = ndefHelper.dumpUint8Array(ndefRecords[i].payload);
ndefContainer.hidden = false;
this.ndefMsgContainer.appendChild(ndefContainer);
}
}
this.ndefMsgContainer.hidden = false;
}
};
window.addEventListener('load', () => NfcReadDemo.init());

View file

@ -0,0 +1,44 @@
var NfcSendDemo = {
tagContainer: null,
init: function nd_init() {
dump('NfcSendDemo init');
var content = document.getElementById('content');
var globalMsg = document.getElementById('global-message');
this.tagContainer = content.querySelector('[data-type="tag-container"]')
var nfc = window.navigator.mozNfc;
if (!nfc) {
globalMsg.textContent = 'NFC API not available.';
return;
}
if (!nfc.enabled) {
globalMsg.textContent = 'NFC is not enabled.';
return;
}
document.getElementById('global-message').textContent = '';
nfc.onpeerfound = this.handlePeerFound.bind(this);
},
handlePeerFound: function nd_handlePeerFound(event) {
var peer = event.peer;
var result = this.tagContainer.querySelector('[data-type="send-result"]');
var ndefHelper = new NDEFHelper();
var url = document.getElementById('urlOptions');
var record = ndefHelper.createURI(url[url.selectedIndex].value);
peer.sendNDEF([record]).then(() => {
result.style.color = "Green";
result.textContent = "Pass";
}).catch((err) => {
result.style.color = "Red";
result.textContent = err;
});
return false;
},
};
window.addEventListener('load', () => NfcSendDemo.init());

View file

@ -0,0 +1,200 @@
var NfcTechDemo = {
init: function nd_init() {
dump('NfcTechDemo init');
var content = document.getElementById('content');
var errorMsg = document.getElementById('error-message');
var nfc = window.navigator.mozNfc;
if (!nfc) {
errorMsg.textContent = 'NFC API not available.';
return;
}
if (!nfc.enabled) {
errorMsg.textContent = 'NFC is not enabled.';
return;
}
document.getElementById('isoDep-container').hidden = true;
document.getElementById('nfcA-container').hidden = true;
nfc.ontagfound = this.handleTagFound.bind(this);
nfc.ontaglost = this.handleTagLost.bind(this);
},
handleTagFound: function nd_handleTagFound(event) {
// clear success/error message.
var errorMsg = document.getElementById('error-message');
errorMsg.textContent = '';
var sucMsg = document.getElementById('success-message');
sucMsg.textContent = '';
document.getElementById('isoDep-container').hidden = true;
document.getElementById('nfcA-container').hidden = true;
var tag = event.tag;
var tagContainer = document.getElementById('tag-container');
var techList = tagContainer.querySelector('[data-type="tech-list"]');
techList.textContent = tag.techList || 'null';
if (tag.techList.indexOf('ISO-DEP') != -1) {
this.processIsoDep(tag);
sucMsg.textContent = 'transceive ISO-DEP succeeds.';
} else if (tag.techList.indexOf('NFC-A') != -1 && tag.type) {
this.processNfcA(tag);
sucMsg.textContent = 'transceive NFCA succeeds.';
}
return false;
},
handleTagLost: function nd_handleTagLost() {
},
processIsoDep: function nd_processIsoDep(tag) {
var container = document.getElementById('isoDep-container');
container.hidden = false;
var isoDep = tag.selectTech('ISO-DEP');
this.getVersion(isoDep, container).then(
() => { return this.getKeyInfo(isoDep, container); });
},
processNfcA: function nd_processNfcA(tag) {
document.getElementById('nfcA-container').hidden = false;
var container;
var nfcA = tag.selectTech('NFC-A');
if (tag.type.toLowerCase() === "type2") {
document.getElementById('type1-container').hidden = true;
document.getElementById('type2-container').hidden = false;
container = document.getElementById('type2-container');
container.querySelector('[data-type="tag-type"]').textContent = tag.type;
this.write1BlockType2(nfcA, container).
then(this.read4BlocksType2(nfcA, container));
} else if (tag.type.toLowerCase() === "type1") {
document.getElementById('type1-container').hidden = false;
document.getElementById('type2-container').hidden = true;
container = document.getElementById('type1-container');
container.querySelector('[data-type="tag-type"]').textContent = tag.type;
this.getUIDType1(nfcA, container);
}
},
getVersion: function nd_getVersion(isoDep, container) {
var ndefHelper = new NDEFHelper();
return new Promise((resolve, reject) => {
isoDep.transceive(new Uint8Array([0x90, 0x60, 0x00, 0x00, 0x00])).
then(response => {
container.querySelector('[data-type="hw-vendor-id"]').textContent =
response[0].toString(16);
container.querySelector('[data-type="hw-type"]').textContent =
response[1].toString(16) + '.' + response[2].toString(16);
container.querySelector('[data-type="hw-version"]').textContent =
response[3].toString(16) + '.' + response[4].toString(16);
container.querySelector('[data-type="hw-storage"]').textContent =
1 << (response[5] >> 1);
container.querySelector('[data-type="hw-protocol"]').textContent =
response[6].toString(16);
// Assuming the status code is 0x91, 0xaf,
// sending 0xaf to get additional info.
return isoDep.transceive(new Uint8Array([0x90, 0xaf, 0x00, 0x00, 0x00])); }).
then(response => {
container.querySelector('[data-type="sw-vendor-id"]').textContent =
response[0].toString(16);
container.querySelector('[data-type="sw-type"]').textContent =
response[1].toString(16) + '.' + response[2].toString(16);
container.querySelector('[data-type="sw-version"]').textContent =
response[3].toString(16) + '.' + response[4].toString(16);
container.querySelector('[data-type="sw-storage"]').textContent =
1 << (response[5] >> 1);
container.querySelector('[data-type="sw-protocol"]').textContent =
response[6].toString(16);
// Assuming the status code is 0x91, 0xaf,
// sending 0xaf to get additional info.
return isoDep.transceive(new Uint8Array([0x90, 0xaf, 0x00, 0x00, 0x00])); }).
then(response => {
// Assuming it uses 7 bytes UID, if a random ID is used, it will be of
// 4 bytes.
container.querySelector('[data-type="uid"]').textContent =
ndefHelper.dumpUint8Array(response.subarray(0, 7));
container.querySelector('[data-type="batch-num"]').textContent =
ndefHelper.dumpUint8Array(response.subarray(7, 12));
container.querySelector('[data-type="production-date"]').textContent =
'Week ' + response[12].toString(16) + '/ 20' + response[13].toString(16);
resolve();
});
});
},
getKeyInfo: function nd_getKeyInfo(isoDep, container) {
return new Promise((resolve, reject) => {
isoDep.transceive(new Uint8Array([0x90, 0x45, 0x00, 0x00, 0x00])).
then(response => {
container.querySelector('[data-type="key-settings"]').textContent =
response[0].toString(16);
container.querySelector('[data-type="max-keys"]').textContent =
response[1].toString(16);
return isoDep.transceive(new Uint8Array([0x90, 0x64, 0x00, 0x00, 0x01, 0x00, 0x00])); }).
then(response => {
container.querySelector('[data-type="key-version"]').textContent =
response[0].toString(16);
resolve();
});
});
},
read4BlocksType2: function nd_read4BlocksType2(nfcA, container) {
return new Promise((resolve, reject) => {
var command = new Uint8Array([0x30, 0x05]);
nfcA.transceive(command).
then(response => {
var ndefHelper = new NDEFHelper();
container.querySelector('[data-type="read"]').textContent =
ndefHelper.dumpUint8Array(response);
resolve();
});
});
},
write1BlockType2: function nd_write1BlockType2(nfcA, container) {
return new Promise((resolve, reject) => {
var command = new Uint8Array([0xA2, 0x05, 0x01, 0x02, 0x03, 0x04]);
nfcA.transceive(command).
then(response => {
var ndefHelper = new NDEFHelper();
container.querySelector('[data-type="write"]').textContent =
ndefHelper.dumpUint8Array(command.subarray(2,6));
resolve();
});
});
},
getUIDType1: function nd_getUIDType1(nfcA, container) {
return new Promise((resolve, reject) => {
var command = new Uint8Array([0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
nfcA.transceive(command).
then(response => {
container.querySelector('[data-type="hr0"]').textContent =
response[0].toString(16);
container.querySelector('[data-type="hr1"]').textContent =
response[1].toString(16);
container.querySelector('[data-type="uid0"]').textContent =
response[2].toString(16);
container.querySelector('[data-type="uid1"]').textContent =
response[3].toString(16);
container.querySelector('[data-type="uid2"]').textContent =
response[4].toString(16);
container.querySelector('[data-type="uid3"]').textContent =
response[5].toString(16);
resolve();
});
});
},
};
window.addEventListener('load', () => NfcTechDemo.init());

View file

@ -0,0 +1,44 @@
var NfcWriteDemo = {
tagContainer: null,
init: function nd_init() {
dump('NfcWriteDemo init');
var content = document.getElementById('content');
var globalMsg = document.getElementById('global-message');
this.tagContainer = content.querySelector('[data-type="tag-container"]')
var nfc = window.navigator.mozNfc;
if (!nfc) {
globalMsg.textContent = 'NFC API not available.';
return;
}
if (!nfc.enabled) {
globalMsg.textContent = 'NFC is not enabled.';
return;
}
document.getElementById('global-message').textContent = '';
nfc.ontagfound = this.handleTagFound.bind(this);
},
handleTagFound: function nd_handleTagFound(event) {
var tag = event.tag;
var result = this.tagContainer.querySelector('[data-type="write-result"]');
var ndefHelper = new NDEFHelper();
var url = document.getElementById('urlOptions');
var record = ndefHelper.createURI(url[url.selectedIndex].value);
tag.writeNDEF([record]).then(() => {
result.style.color = "Green";
result.textContent = "Pass";
}).catch((err) => {
result.style.color = "Red";
result.textContent = err;
});
return false;
},
};
window.addEventListener('load', () => NfcWriteDemo.init());

View file

@ -0,0 +1,141 @@
'use stricts';
var NfcTest = {
get APItests() {
delete this.APItests;
return this.APItests = document.getElementById('API-tests');
},
get iframe() {
delete this.iframe;
return this.iframe = document.getElementById('test-iframe');
},
get backBtn() {
delete this.backBtn;
return this.backBtn = document.getElementById('test-panel-back');
},
get panelTitle() {
delete this.panelTitle;
return this.panelTitle = document.getElementById('test-panel-title');
},
get tabs() {
delete this.tabs;
return this.tabs = document.querySelectorAll('[role="tab"]');
},
get APITab() {
delete this.APITab;
return this.APITab = document.getElementById('API');
},
currentTab: 'API',
handleNotificationMessage: function(message) {
if (!message.clicked) {
return;
}
// handle notifications when this app is closed
navigator.mozApps.getSelf().onsuccess = function gotSelf(evt) {
var app = evt.target.result;
app.launch();
};
},
init: function ut_init() {
this.iframe.addEventListener('load', this);
this.iframe.addEventListener('unload', this);
document.body.addEventListener('transitionend', this);
window.addEventListener('keyup', this);
window.addEventListener('hashchange', this);
this.backBtn.addEventListener('click', this);
navigator.mozSetMessageHandler('notification', function(msg) {
this.handleNotificationMessage(msg);
}.bind(this));
var name = this.getNameFromHash();
if (name) {
this.openTest(name);
}
else {
// if no test is specified, load UI tests list (select UI tab)
window.location.hash = 'UI';
}
},
uninit: function ut_uninit() {
this.iframe.removeEventListener('load', this);
this.iframe.removeEventListener('unload', this);
document.body.removeEventListener('transitionend', this);
window.removeEventListener('keyup', this);
window.removeEventListener('hashchange', this);
this.backBtn.removeEventListener('click', this);
},
getNameFromHash: function ut_getNameFromHash() {
return (/\btest=(.+)(&|$)/.exec(window.location.hash) || [])[1];
},
handleEvent: function ut_handleEvent(ev) {
switch (ev.type) {
case 'click':
if (ev.target != this.backBtn) {
return;
}
if (window.location.hash) {
window.location.hash = '';
}
break;
case 'load':
this.iframe.contentWindow.addEventListener('keyup', this);
break;
case 'unload':
this.iframe.contentWindow.removeEventListener('keyup', this);
break;
case 'hashchange':
if (window.location.hash == '#API')
{
this.APItests.classList.remove('invisible');
AccessibilityHelper.setAriaSelected(this.APITab, this.tabs);
if (!this.UItests.classList.contains('invisible'))
this.UItests.classList.add('invisible');
if (!this.HWtests.classList.contains('invisible'))
this.HWtests.classList.add('invisible');
}
else
{
var name = this.getNameFromHash();
if (!name) {
this.closeTest();
return;
}
this.panelTitle.textContent = name;
this.openTest(name);
}
break;
case 'transitionend':
var name = this.getNameFromHash();
if (!name)
this.iframe.src = 'about:blank';
break;
}
},
openTest: function ut_openTest(name) {
// save tab name from URL
// e.g. '#test=UI/empty' => 'UI'
this.currentTab = (/=\b(.+)\//.exec(window.location.hash) || [])[1];
document.body.classList.add('test');
var self = this;
window.setTimeout(function openTestPage() {
self.iframe.src = './tests_html/' + name + '.html';
// we need to focus the iframe to let the key event send to it.
self.iframe.focus();
}, 200);
},
closeTest: function ut_closeTest() {
var isOpened = document.body.classList.contains('test');
if (!isOpened)
return false;
document.body.classList.remove('test');
// select tab after close test iframe
window.location.hash = this.currentTab;
return true;
}
};
window.addEventListener('load', NfcTest.init.bind(NfcTest));