VulpesOS/gaia/apps/keyboard/js/settings/layout_dictionary_downloader.js
OverSu 200d1fb235 Release Android Preview 21 and bilingual changelog
Add permission-based reading of Android contacts, SMS and call history. Include shared Gaia fixes and document device validation limits. Pixel image qualification remains pending.
2026-10-14 13:13:46 +02:00

105 lines
3 KiB
JavaScript
Executable file

'use strict';
(function(exports) {
var LayoutDictionaryDownloader = function(imEngineId, dictFilename) {
this.imEngineId = imEngineId;
this.dictFilename = dictFilename;
this._xhr = null;
};
// Dictionaries ship with Vulpes so adding a layout also works offline.
LayoutDictionaryDownloader.prototype.REMOTE_URL =
'/js/imes/%imEngineId/dictionaries/%dictFilename';
// If the data schema changes in an incompatible way, we must bump the
// version-to-download here.
LayoutDictionaryDownloader.prototype.ENGINE_DATA_VERSIONS = Object.freeze({
'latin': 1
});
LayoutDictionaryDownloader.prototype.onprogress = null;
LayoutDictionaryDownloader.prototype.load = function() {
if (this._xhr) {
throw new Error('LayoutDictionaryDownloader: Already downloading...');
}
var p = new Promise(function(resolve, reject) {
var url = this.REMOTE_URL
.replace('%imEngineId', this.imEngineId)
.replace('%ver', this.ENGINE_DATA_VERSIONS[this.imEngineId])
.replace('%dictFilename', this.dictFilename);
var xhr = this._xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onprogress = function(evt) {
if (typeof this.onprogress === 'function') {
this.onprogress(evt.loaded, evt.total);
}
}.bind(this);
xhr.onloadend = function() {
this._xhr = null;
var data = xhr.response;
// We will only resolve the promise when the data is verified.
// For any other situration, we simply return the HTTP status code as
// a rejected placeholder value.
// (it doesn't really matter if you got 200/404/500
// or anything if it's not what you are looking for right :-/?)
if (data && this._verifyData(data)) {
if (typeof this.onprogress === 'function') {
this.onprogress(data.byteLength, data.byteLength);
}
resolve(data);
} else {
reject(xhr.statusText);
}
}.bind(this);
xhr.send();
}.bind(this));
return p;
};
LayoutDictionaryDownloader.prototype.abort = function() {
if (!this._xhr) {
throw new Error('LayoutDictionaryDownloader: ' +
'Not downloading but abort is called.');
}
this._xhr.abort();
this._xhr = null;
};
LayoutDictionaryDownloader.prototype._verifyData = function(buffer) {
switch (this.imEngineId) {
case 'latin':
// There is only first 8 bytes to check -- we can't be sure if the data
// is truncated. However that shouldn't happen since our CDN sends out
// Content-Length header.
//
// TODO: Harden the verification here in some way.
//
if (!(buffer instanceof ArrayBuffer) || buffer.byteLength < 8) {
return false;
}
var view = new Uint8Array(buffer, 0, 8);
return (String.fromCharCode.apply(String, view) === 'FxOSDICT');
default:
throw new Error('LayoutDictionaryDownloader: ' +
'No verify method available for this imEngine.');
}
};
exports.LayoutDictionaryDownloader = LayoutDictionaryDownloader;
}(window));