VulpesOS/gaia/apps/keyboard/js/settings/layout_dictionary_downloader.js
OverSu b38dea0597 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.
2026-09-30 23:13:03 +02:00

106 lines
3.1 KiB
JavaScript
Executable file

'use strict';
(function(exports) {
var LayoutDictionaryDownloader = function(imEngineId, dictFilename) {
this.imEngineId = imEngineId;
this.dictFilename = dictFilename;
this._xhr = null;
};
// Remote URLs to download the data. We require the remote server to have
// CORS turned on since we are not asking for SystemXHR permission.
LayoutDictionaryDownloader.prototype.REMOTE_URL =
'https://fxos.cdn.mozilla.net/dictionaries/%imEngineId/%ver/%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));