VulpesOS/gaia/build/svoperapps.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

326 lines
10 KiB
JavaScript
Executable file

'use strict';
var utils = require('./utils');
var SvoperappsBuilder = function() {
};
SvoperappsBuilder.prototype.STAGE_SVOPERAPPS_FOLDER = '__svoperapps';
SvoperappsBuilder.prototype.APPS_DATA_FOLDER = 'cached';
SvoperappsBuilder.prototype.APPS_CONF_FOLDER = 'conf';
SvoperappsBuilder.prototype.PROFILE_APPS_FOLDER = 'svoperapps';
SvoperappsBuilder.prototype.APPS_CONF_FILENAME = 'singlevariantconf.json';
SvoperappsBuilder.prototype.DEFAULT_EMPTY_SCREENS = 2;
SvoperappsBuilder.prototype.MAX_ICONS_PER_PAGE = 4 * 4;
SvoperappsBuilder.prototype.errorObject = null;
/*
* the count of current issued and unprocessed app requests.
*/
SvoperappsBuilder.prototype.appRequest = 0;
/*
* check and cut the path parts out from url
* if the url does not conform to {scheme}://{net_loc}/, this function returns
* null.
*/
SvoperappsBuilder.prototype.getOrigin = function(url, defaultOrigin) {
if (!url) {
return null;
} else {
var matched = url.match(/^([a-zA-Z]+:\/\/.[^/]+).*/);
return (matched && matched.length > 1) ? matched[1] : defaultOrigin;
}
};
/*
* check if the downloaded app folder contains the following files:
* 1. metadata.json, 2. update.webapp, 3. application.zip, 4. manifest.webapp.
*/
SvoperappsBuilder.prototype.checkDownloadedApp = function(path) {
var files = utils.listFiles(path, utils.FILE_TYPE_FILE, false);
var metadataFound = false;
var updateFound = false;
var applicationFound = false;
var manifestFound = false;
for (var i = 0; i < files.length; i++) {
if (files[i].match(/[\/\\]metadata\.json$/)) {
metadataFound = true;
} else if (files[i].match(/[\/\\]update\.webapp$/)) {
updateFound = true;
} else if (files[i].match(/[\/\\]application\.zip$/)) {
applicationFound = true;
} else if (files[i].match(/[\/\\]manifest\.webapp$/)) {
manifestFound = true;
}
}
return metadataFound && ((updateFound && applicationFound )|| manifestFound);
};
/*
* Download the packaged app from manifest.package_path to cache path.
*/
SvoperappsBuilder.prototype.downloadPackageApp =
function(manifest, metadata, appPath, cb, errCb) {
utils.log('svoperapps.js',
'download packaged app from: ' + manifest.package_path);
// write update.webapp
utils.writeContent(utils.getFile(appPath, 'update.webapp'),
JSON.stringify(manifest));
// write metadata.json
utils.writeContent(utils.getFile(appPath, 'metadata.json'),
JSON.stringify(metadata));
// download application
var origin = utils.getNewURI(metadata.manifestURL, null, null);
var absolutePkgPath = origin.resolve(manifest.package_path);
utils.download(absolutePkgPath, appPath + 'application.zip', cb,
function err(url, path) {
errCb(new Error('unable to download: ' + url));
});
};
SvoperappsBuilder.prototype.prepareHostedApp =
function(manifest, metadata, appPath, callback, errCallback) {
// Bug 936028 - appcache needs to be removed from manifest
if (manifest.appcache_path) {
delete manifest.appcache_path;
}
// write manifest
utils.writeContent(utils.getFile(appPath, 'manifest.webapp'),
JSON.stringify(manifest));
// write metadata
utils.writeContent(utils.getFile(appPath, 'metadata.json'),
JSON.stringify(metadata));
callback();
};
SvoperappsBuilder.prototype.downloadApp =
function(manifest, metadata, appPath, callback, errCallback) {
// Determine if application is hosted or packaged and save manifest
if (manifest.package_path) { // packaged app
this.downloadPackageApp(manifest, metadata, appPath, callback, errCallback);
} else if (metadata.origin) { // hosted app
this.prepareHostedApp(manifest, metadata, appPath, callback, errCallback);
} else { // error case
errCallback(new Error('installOrigin required for app ' + manifest.name +
' in local_apps.json configuration file'));
}
};
SvoperappsBuilder.prototype.processApp =
function(appId, manifestUrl, origin, installOrigin, appPath, cached, done) {
var self = this;
var destPath = utils.joinPath(this.profilePath, this.PROFILE_APPS_FOLDER);
// When everything is ready, we call this function to copy all files in
// appPath to profilePath.
function copyAppPathToProfile() {
try {
utils.copyFileTo(appPath, destPath, appId);
} catch (ex) {
utils.log('svoperapps.js', 'copy file error: ' + ex.message);
self.errorObject = ex;
} finally {
done();
}
}
function handleError() {
self.errorObject = new Error('failed to download manifest from ' +
manifestUrl);
done();
}
utils.log('svoperapps.js', 'downloading manifest: ' + manifestUrl);
utils.downloadJSON(manifestUrl, function mandifestHandler(json) {
if (!json) {
if (cached) {
// if json is not downloaded but we already had everything, just copy
// it.
copyAppPathToProfile();
} else {
handleError();
}
return;
}
// prepare the folders for this app
utils.mkdirs(appPath);
// if the app is cached, we need to compare the manifest file.
if (cached) {
// If we already have everything, we still need to compare the
// update/manifest with downloaded content. If they are identical, we
// can view them as the same. If not, we need to download and rebuild
// them.
var updatePath = utils.joinPath(appPath, 'update.webapp');
var manifestFile = utils.fileExists(updatePath) ?
'update.webapp' : 'manifest.webapp';
var fileJSON = utils.readJSONFromPath(utils.joinPath(appPath,
manifestFile));
// Bug 936028 - Comparation has to be done removing appcache + "\n");
if (json.appcache_path) {
delete json.appcache_path;
}
if (JSON.stringify(json) === JSON.stringify(fileJSON)) {
copyAppPathToProfile();
return;
}
}
try {
// if it is cached or the manifest file is not the same, we need to
// download that app.
var metadata = {
'id': appId,
'installOrigin': installOrigin,
'manifestURL': manifestUrl
};
metadata.origin = json.package_path ?
self.getOrigin(json.package_path, installOrigin) :
(origin || self.getOrigin(json.launch_path));
self.downloadApp(json, metadata, appPath,
copyAppPathToProfile, handleError);
} catch (ex) {
// error found
self.errorObject = ex;
done();
}
});
};
SvoperappsBuilder.prototype.fetchApps = function(done) {
utils.log('svoperapps.js', 'fetch apps');
var apps = this.variant.apps;
var distPath = utils.joinPath(this.svoperappsStagePath,
this.APPS_DATA_FOLDER);
var cachedApps = utils.listFiles(distPath, utils.FILE_TYPE_DIRECTORY, false);
var self = this;
function checkApp(cachedApp) {
if (cachedApp.match(new RegExp('[\\\/]' + app + '$'))) {
cachedApps.splice(cachedApps.indexOf(cachedApp), 1);
//compare everything.
if (self.checkDownloadedApp(appPath)) {
utils.log('svoperapps.js', app + ' is cached.');
// path exists and everything is ok, make it as cached
appCached = true;
} else {
// path exists but some files are missing, mark it as not cached.
appCached = false;
utils.deleteFile(appPath, true);
}
return false;
} else {
return true;
}
}
function processAppCallback() {
self.appRequest--;
if (self.appRequest === 0) {
// all apps are processed, clean all left cached apps, those are not
// found with the app list.
for (var i = 0; i < cachedApps.length; i++) {
utils.log('svoperapps.js', 'remove useless cache: ' + cachedApps[i]);
utils.deleteFile(cachedApps[i], true);
}
done();
}
}
for (var app in apps) {
var appPath = utils.joinPath(distPath, app);
var appCached = false;
// check if the appPath already exists in cachedApp list
cachedApps.every(checkApp);
var origin = apps[app].origin || null;
var installOrigin = apps[app].installOrigin || null;
this.appRequest++;
this.processApp(app, apps[app].manifestURL, origin, installOrigin,
appPath, appCached, processAppCallback);
}
if (Object.keys(apps).length === 0) {
done();
}
};
SvoperappsBuilder.prototype.prepareDir = function() {
var profilePath = this.options.PROFILE_DIR;
var distributionPath = this.options.GAIA_DISTRIBUTION_DIR;
var svoperappsStagePath = utils.joinPath(this.options.GAIA_DIR, 'build_stage',
this.STAGE_SVOPERAPPS_FOLDER);
utils.mkdirs(svoperappsStagePath);
// create the temp folders.
utils.mkdirs(utils.joinPath(svoperappsStagePath, this.APPS_DATA_FOLDER));
// clear profile folder
utils.deleteFile(utils.joinPath(profilePath, this.PROFILE_APPS_FOLDER), true);
utils.mkdirs(utils.joinPath(profilePath, this.PROFILE_APPS_FOLDER));
this.profilePath = profilePath;
this.distributionPath = distributionPath;
this.svoperappsStagePath = svoperappsStagePath;
};
SvoperappsBuilder.prototype.setOptions = function(options) {
this.options = options;
this.variant = utils.readJSONFromPath(options.VARIANT_PATH);
};
SvoperappsBuilder.prototype.generateConfig = function() {
var file = utils.getFile(this.options.PROFILE_DIR, this.PROFILE_APPS_FOLDER,
this.APPS_CONF_FILENAME);
var config = {};
if (this.variant.operators) {
this.variant.operators.forEach(function(operator) {
var apps = operator.apps.map(function(app) {
return app.id;
});
operator['mcc-mnc'].forEach(function(mccmnc) {
config[mccmnc] = apps;
});
});
}
utils.writeContent(file, JSON.stringify(config));
};
SvoperappsBuilder.prototype.execute = function(options) {
if (!options.PROFILE_DIR || !options.GAIA_DISTRIBUTION_DIR ||
!options.VARIANT_PATH) {
utils.log('svoperapps.js', 'PROFILE_DIR, GAIA_DISTRIBUTION_DIR,' +
' VARIANT_PATH are all required');
return;
}
this.setOptions(options);
this.prepareDir();
this.generateConfig();
// set done flag and start everything.
var done = false;
// read variant.json
// download all apps
this.fetchApps(function() {
done = true;
});
// Ensure everything is finished before exit this module
utils.processEvents(function exitFunc() {
return {
'wait': !done,
'error': this.errorObject
};
}.bind(this));
};
exports.execute = function(options) {
(new SvoperappsBuilder()).execute(options);
};