VulpesOS/gaia/shared/js/smart-screen/applications.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

469 lines
13 KiB
JavaScript
Executable file

'use strict';
/* global evt, ManifestHelper */
(function(exports) {
/**
* Application Entry Point
*
* This object stands for an entry point of the app. There can be more than
* one entry point in one app. Each entry point can own its name and icon, and
* can be launched singly.
*
* @property {String} manifestURL The app's manifestURL.
* @property {String} entryPoint The entry point defined in app's manifest.
* @property {String} name The name of the entry.
* @typedef {Object} AppEntryPoint
*/
/**
* Applications is a helper for mozApps.mgmt. It is responsible for:
*
* - get all installed apps
* - get app icon blob
* - listen to app install/update/uninstall events and publish them
* - launch app
*
* @namespace Applications
* @requires ManifestHelper
*/
var Applications = evt({
/**
* App roles which we don't need to handle and monitor.
*
* @constant
* @type {Array}
* @memberof Applications
*/
HIDDEN_ROLES: [
'system', 'homescreen', 'input', 'deck', 'addon', 'langpack', 'widget'
],
/**
* Default icon url.
*
* @constant
* @type {String}
* @memberof Applications
*/
DEFAULT_ICON_URL: '/style/images/default.png',
/**
* Installed apps indexed by their "manifestURL".
*
* @readonly
* @type {Object}
* @memberof Applications
*/
installedApps: {},
_ready: false,
_readyCallbacks: [],
_isHiddenApp: function appIsHiddenApp(role) {
if (!role) {
return false;
}
return (this.HIDDEN_ROLES.indexOf(role) !== -1);
},
_loadIcon: function appLoadIcon(request) {
if (!request.url) {
if (request.onerror) {
request.onerror();
}
return;
}
var xhr = new XMLHttpRequest({
mozAnon: true,
mozSystem: true
});
var icon = request.url;
xhr.open('GET', icon, true);
xhr.responseType = 'blob';
xhr.onload = function onload(evt) {
var status = xhr.status;
if (status !== 0 && status !== 200) {
if (request.onerror) {
request.onerror();
}
return;
}
if (request.onsuccess) {
request.onsuccess(xhr.response);
}
};
xhr.ontimeout = xhr.onerror = function onerror(evt) {
if (request.onerror) {
request.onerror();
}
};
try {
xhr.send(null);
} catch (evt) {
if (request.onerror) {
request.onerror();
}
}
},
_bestMatchingIcon:
function appBestMatchingIcon(app, manifest, preferredSize) {
preferredSize = preferredSize || Number.MAX_VALUE;
var max = 0;
var closestSize = 0;
for (var size in manifest.icons) {
size = parseInt(size, 10);
if (size > max) {
max = size;
}
if (!closestSize && size >= preferredSize) {
closestSize = size;
}
}
if (! closestSize) {
closestSize = max;
}
var url = manifest.icons[closestSize];
if (!url) {
return;
}
if (url.indexOf('data:') === 0 ||
url.indexOf('app://') === 0 ||
url.indexOf('http://') === 0 ||
url.indexOf('https://') === 0) {
return url;
}
if (url.charAt(0) != '/') {
console.warn('`' + manifest.name + '` app icon is invalid. ' +
'Manifest `icons` attribute should contain URLs -or- ' +
'absolute paths from the origin field.');
return '';
}
if (app.origin.slice(-1) === '/') {
return app.origin.slice(0, -1) + url;
}
return app.origin + url;
},
/**
* An easy way to register callback functions for "ready" event.
*
* Use this method to initialize your code if it depends on Applications. It
* will be triggered immediately if Applications is already ready.
*
* @param {Function} callback This function will be called when initialized.
* @memberof Applications
*/
ready: function appReady(callback) {
if (this._ready) {
window.setTimeout(callback);
} else {
this._readyCallbacks.push(callback);
}
},
onAppInstall: function onAppInstall(evt) {
var app = evt.application;
var manifest = app.manifest || app.updateManifest;
if (!app.launch || !manifest || !manifest.icons ||
this._isHiddenApp(manifest.role)) {
return;
}
var message =
this.installedApps[app.manifestURL] ? 'update' : 'install';
this.installedApps[app.manifestURL] = app;
this.fire(message, this.getAppEntries(app.manifestURL));
},
onAppUninstall: function onAppUninstall(evt) {
var app = evt.application;
if (this.installedApps[app.manifestURL]) {
this.fire('uninstall', this.getAppEntries(app.manifestURL));
delete this.installedApps[app.manifestURL];
}
},
/**
* Initialize Applications.
*
* The callback functions registered by "ready" method will be triggered
* after it's done.
*
* @param {Function} callback This function will be called when "init" is
* done.
* @memberof Applications
*/
init: function appInit(callback) {
var appMgmt = navigator.mozApps.mgmt;
var self = this;
appMgmt.getAll().onsuccess = function onsuccess(event) {
event.target.result.forEach(function eachApp(app) {
var manifest = app.manifest;
if (!app.launch || !manifest || !manifest.icons ||
self._isHiddenApp(manifest.role)) {
return;
}
self.installedApps[app.manifestURL] = app;
});
self._ready = true;
if (callback) {
callback();
}
while (self._readyCallbacks.length) {
setTimeout(self._readyCallbacks.shift());
}
};
appMgmt.addEventListener('install', this);
appMgmt.addEventListener('uninstall', this);
},
/**
* Release all resources.
*
* @memberof Applications
*/
uninit: function appUninit() {
var appMgmt = navigator.mozApps.mgmt;
appMgmt.oninstall = null;
appMgmt.onuninstall = null;
this.installedApps = {};
this._ready = false;
this._readyCallbacks = [];
},
/* jshint -W004 */
// XXX: Uses this to prevent 'helper' is already defined jshint error
/**
* Get all "entry_point"s from the specified app.
*
* @param {String} manifestURL The app's manifestURL.
* @return {Array} An array contains all {@link AppEntryPoint} of the
* specified app.
* @memberof Applications
*/
getAppEntries: function appGetAppEntries(manifestURL) {
if (!manifestURL || !this.installedApps[manifestURL]) {
return [];
}
var manifest = this.installedApps[manifestURL].manifest ||
this.installedApps[manifestURL].updateManifest;
var entryPoints = manifest.entry_points;
var entries = [];
var removable = this.installedApps[manifestURL].removable;
if (!entryPoints || manifest.type !== 'certified') {
var helper = new ManifestHelper(manifest);
entries.push({
manifestURL: manifestURL,
entryPoint: '',
name: helper.name,
removable: removable
});
} else {
for (var entryPoint in entryPoints) {
if (entryPoints[entryPoint].icons) {
var helper = new ManifestHelper(entryPoints[entryPoint]);
entries.push({
manifestURL: manifestURL,
entryPoint: entryPoint,
name: helper.name,
removable: removable
});
}
}
}
return entries;
},
/* jshint +W004 */
/**
* Get all "entry_point"s from all installed apps.
*
* @return {Array} An array contains all {@link AppEntryPoint} of the
* installed apps.
* @memberof Applications
*/
getAllAppEntries: function appGetAllAppEntries() {
if (!this._ready) {
return null;
}
var entries = [];
for (var manifestURL in this.installedApps) {
entries.push.apply(entries, this.getAppEntries(manifestURL));
}
return entries;
},
/**
* Launch an app by its manifestURL and entryPoint.
*
* @param {String} manifestURL The app's manifestURL.
* @param {String} [entryPoint] The specific "entry_point" you want to
* launch. Default launch path will be involved
* if it's omitted.
* @return {Boolean} true if succeed.
* @memberof Applications
*/
launch: function appLaunch(manifestURL, entryPoint) {
var installedApps = this.installedApps;
if (!manifestURL || !installedApps[manifestURL] ||
!installedApps[manifestURL].launch) {
return false;
}
entryPoint = entryPoint || '';
installedApps[manifestURL].launch(entryPoint);
return true;
},
/**
* Get manifest object by specified manifestURL and entryPoint.
*
* @param {String} manifestURL The app's manifestURL.
* @param {String} [entryPoint] Specify an "entry_point" if you want to get
* its part only. Otherwise, the whole manifest
* object will be returned.
* @return {Object} A manifest object or "null" if the app doesn't exist.
* @memberof Applications
*/
getEntryManifest: function appGetEntryManifest(manifestURL, entryPoint) {
if (!manifestURL || !this.installedApps[manifestURL]) {
return null;
}
var manifest = this.installedApps[manifestURL].manifest ||
this.installedApps[manifestURL].updateManifest;
if (entryPoint) {
var entry_manifest = manifest.entry_points[entryPoint];
return entry_manifest || null;
}
return manifest;
},
/**
* Get app/entry's name.
*
* @param {String} manifestURL An app's manifestURL.
* @param {String} [entryPoint] Specify an "entry_point" if you want to get
* its part only. Otherwise, the default name
* will be returned.
* @return {String} The app/entry's name or empty string if the app doesn't
* exist.
* @memberof Applications
*/
getName: function appGetName(manifestURL, entryPoint) {
var entry_manifest = this.getEntryManifest(manifestURL, entryPoint);
if (!entry_manifest) {
return '';
}
return new ManifestHelper(entry_manifest).name;
},
/**
* Get blob data of the app/entry's icon.
*
* @param {String} manifestURL An app's manifestURL.
* @param {String} [entryPoint] Specify an "entry_point".
* @param {Number} [preferredSize=Number.MAX_VALUE]
* In pixels. This method will choose the smallest icon that its
* width greater than this value or the biggest one if all the icons'
* width are smaller than this value.
* @param {Function} [callback] The callback will contains blob data as the
* first argument if succeed or "undefined" if
* something failed.
* @return {Boolean} true if the process is started or false if no valid
* icon url can be found or the app doesn't exist.
* @memberof Applications
*/
getIconBlob: function appGetIconBlob(manifestURL, entryPoint, preferredSize,
callback) {
var entry_manifest = this.getEntryManifest(manifestURL, entryPoint);
if (! entry_manifest) {
return false;
}
var url = this._bestMatchingIcon(
this.installedApps[manifestURL], entry_manifest, preferredSize);
if (!url) {
if (callback) {
setTimeout(callback);
}
return true;
}
this._loadIcon({
url: url,
onsuccess: function(blob) {
if (callback) {
callback(blob);
}
},
onerror: function() {
if (callback) {
callback();
}
}
});
return true;
},
listAppsByRole: function listAppsByRole(role) {
var matched = [];
for (var manifestURL in this.installedApps) {
var app = this.installedApps[manifestURL];
if (!role || app.manifest.role === role) {
matched.push({
'manifestURL': manifestURL,
'app': app
});
}
}
return matched;
},
handleEvent: function handleEvent(evt) {
switch(evt.type) {
case 'install':
this.onAppInstall(evt);
break;
case 'uninstall':
this.onAppUninstall(evt);
break;
}
}
});
exports.Applications = Applications;
})(window);