VulpesOS/gaia/tests/atoms/gaia_apps.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

441 lines
No EOL
14 KiB
JavaScript
Executable file

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
/* globals marionetteScriptFinished, waitFor */
var GaiaApps = {
normalizeName: function(name) {
return name.replace(/[- ]+/g, '').toLowerCase();
},
getInstalledApps: function() {
let req = navigator.mozApps.mgmt.getAll();
req.onsuccess = function() {
marionetteScriptFinished(req.result);
};
},
getRunningApps: function(includeSystemApps) {
let runningApps = GaiaApps.getApps(includeSystemApps);
// Return a simplified version of the runningApps object which can be
// JSON-serialized.
let apps = {};
for (let app in runningApps) {
let anApp = {};
for (let key in runningApps[app]) {
if (['name', 'origin', 'manifest'].indexOf(key) > -1) {
anApp[key] = runningApps[app][key];
}
}
apps[runningApps[app].origin] = anApp;
}
return apps;
},
getApps: function(includeSystemApps) {
let service = window.wrappedJSObject.Service;
let apps = includeSystemApps ?
service.query('getApps') :
service.query('snapshot');
return apps;
},
getRunningAppOrigin: function(name) {
let apps = GaiaApps.getApps(true);
for (let id in apps) {
if (apps[id].name == name) {
return apps[id].origin;
}
}
return undefined;
},
getPermission: function(appName, permissionName) {
GaiaApps.locateWithName(appName, function(app) {
console.log('Getting permission \'' +
permissionName +
'\' for ' +
appName);
var mozPerms = navigator.mozPermissionSettings;
var result = mozPerms.get(
permissionName, app.manifestURL, app.origin, false
);
marionetteScriptFinished(result);
});
},
setPermission: function(appName, permissionName, value) {
GaiaApps.locateWithName(appName, function(app) {
console.log('Setting permission \'' + permissionName + '\' for ' +
appName + 'to \'' + value + '\'');
var mozPerms = navigator.mozPermissionSettings;
mozPerms.set(
permissionName, value, app.manifestURL, app.origin, false
);
marionetteScriptFinished();
});
},
setPermissionByUrl: function(manifestUrl, permissionName, value, entryPoint) {
GaiaApps.locateWithManifestURL(manifestUrl, entryPoint, function(app) {
console.log('Setting permission \'' + permissionName + '\' for ' +
manifestUrl + 'to \'' + value + '\'');
var mozPerms = navigator.mozPermissionSettings;
mozPerms.set(
permissionName, value, app.manifestURL, app.origin, false
);
marionetteScriptFinished();
});
},
sendLocateResponse:
function(aCallback, app, appName, launchPath, entryPoint) {
var callback = aCallback || marionetteScriptFinished;
if (callback === marionetteScriptFinished) {
var result = false;
if (typeof(app) === 'object') {
result = {
name: app.manifest.name,
origin: app.origin,
entryPoint: entryPoint || null,
normalizedName: appName
};
}
callback(result);
} else {
callback(app, appName, launchPath, entryPoint);
}
},
locateWithName: function(name, aCallback) {
var callback = aCallback || marionetteScriptFinished;
let apps = window.wrappedJSObject.applications ||
window.wrappedJSObject.Applications;
let installedApps = apps.installedApps;
let normalizedSearchName = GaiaApps.normalizeName(name);
for (let manifestURL in installedApps) {
let app = installedApps[manifestURL];
let entryPoints = app.manifest.entry_points;
if (entryPoints) {
for (let ep in entryPoints) {
let currentEntryPoint = entryPoints[ep];
let appName = currentEntryPoint.name;
let launchPath = currentEntryPoint.launch_path;
let locales = currentEntryPoint.locales;
if (normalizedSearchName === GaiaApps.normalizeName(appName)) {
return GaiaApps.sendLocateResponse(callback,
app,
appName,
launchPath,
ep);
} else if (locales) {
for (let id in locales) {
let localisedAppName = locales[id].name;
if (localisedAppName && normalizedSearchName ===
GaiaApps.normalizeName(localisedAppName)) {
return GaiaApps.sendLocateResponse(callback,
app,
appName,
launchPath,
ep);
}
}
}
}
} else {
let appName = app.manifest.name;
let launchPath = app.manifest.launch_path;
let locales = app.manifest.locales;
if (normalizedSearchName === GaiaApps.normalizeName(appName)) {
return GaiaApps.sendLocateResponse(callback,
app,
appName,
launchPath);
} else if (locales) {
for (let id in locales) {
let localisedAppName = locales[id].name;
if (localisedAppName && normalizedSearchName ===
GaiaApps.normalizeName(localisedAppName)) {
return GaiaApps.sendLocateResponse(callback,
app,
appName,
launchPath);
}
}
}
}
}
callback(false);
},
locateWithManifestURL: function(manifestURL, entryPoint, aCallback) {
var callback = aCallback || marionetteScriptFinished;
var apps = window.wrappedJSObject.applications ||
window.wrappedJSObject.Applications;
var app = apps.getByManifestURL(manifestURL);
var launchPath;
if (app != null) {
GaiaApps._formatApp(app, manifestURL, entryPoint, callback, launchPath);
} else {
callback(false);
}
},
_formatApp: function(app, manifestURL, entryPoint, callback, launchPath) {
if (entryPoint) {
if (app.manifest.entry_points[entryPoint]) {
launchPath = app.manifest.entry_points[entryPoint].launch_path;
} else {
app = null;
}
} else {
launchPath = app.manifest.launch_path;
}
GaiaApps.sendLocateResponse(callback, app,
manifestURL, launchPath, entryPoint);
},
// Returns the number of running apps.
// if includeSystemApps is true then system always-running
// apps (eg Homescreen) will be counted
numRunningApps: function(includeSystemApps) {
return Object.keys(GaiaApps.getApps(includeSystemApps)).length;
},
isRunning: function(origin) {
var apps = GaiaApps.getApps(true);
for (var id in apps) {
if (apps[id].origin === origin) {
return true;
}
}
return false;
},
// Kills the specified app.
kill: function(aOrigin, aCallback) {
var callback = aCallback || marionetteScriptFinished;
if (!GaiaApps.isRunning(aOrigin)) {
callback(false);
}
else {
window.addEventListener('appterminated', function gt_onAppTerminated() {
window.removeEventListener('appterminated', gt_onAppTerminated);
waitFor(
function() {
console.log('app with origin \'' + aOrigin + '\' has terminated');
callback(true);
},
function() {
return !GaiaApps.isRunning(aOrigin);
}
);
});
console.log('terminating app with origin \'' + aOrigin + '\'');
window.wrappedJSObject.Service.request('AppWindowManager:kill', aOrigin);
}
},
// Kills all running apps that may be killed by the user, defined as
// being accessible by the Cards View/Stack Manager
killAll: function() {
let originsToClose = [];
let that = this;
let apps = GaiaApps.getApps();
for (let id in apps) {
let origin = apps[id].origin;
originsToClose.push(origin);
}
if (!originsToClose.length) {
marionetteScriptFinished(true);
return;
}
originsToClose.slice(0).forEach(function(origin) {
GaiaApps.kill(origin, function() {});
});
// Even after the 'appterminated' event has been fired for an app,
// it can still exist in the apps list, so wait until 1 or fewer
// apps are running (since we don't close the homescreen app).
waitFor(
function() { marionetteScriptFinished(true); },
function() { return that.numRunningApps() <= 1; }
);
},
launch: function(app, appName, launchPath, entryPoint) {
if (app) {
let manifestURL = app.manifestURL;
let origin = app.origin;
let sendResponse = function() {
let result = GaiaApps.getDisplayedApp();
marionetteScriptFinished(result);
};
let displayedApp = GaiaApps.getDisplayedApp();
if ((displayedApp.manifestURL == manifestURL &&
displayedApp.entryPoint == entryPoint) ||
(!displayedApp.manifestURL && displayedApp.origin == origin)) {
console.log(`app with origin: ${displayedApp.origin} and
manifestURL: ${displayedApp.manifestURL} and
entryPoint: ${displayedApp.entryPoint} is already running`);
sendResponse();
} else {
window.addEventListener('windowopened', function appOpen() {
window.removeEventListener('windowopened', appOpen);
waitFor(
function() {
console.log(`app with origin: ${displayedApp.origin} and
manifestURL: ${displayedApp.manifestURL} and
entryPoint: ${displayedApp.entryPoint} has launched`);
sendResponse();
},
function() {
// wait for the displayed app to have expected manifestURL/origin
let displayedApp = GaiaApps.getDisplayedApp();
if (displayedApp.manifestURL) {
return displayedApp.manifestURL == manifestURL &&
displayedApp.entryPoint == entryPoint;
} else {
return displayedApp.origin == origin;
}
}
);
});
console.log('launching app with name \'' + appName + '\'');
app.launch(entryPoint || null);
}
} else {
marionetteScriptFinished(false);
}
},
// Launches app with the specified name (e.g., 'Calculator'); returns the
// an object with the app frame if successful, false if the app can't be
// found, or times out if the app frame can't be found after launching the
// app.
launchWithName: function(name) {
GaiaApps.locateWithName(name, this.launch.bind(this));
},
// Launches app with the specified manifestURL. returns the
// an object with the app frame if successful, false if the app can't be
// found, or times out if the app frame can't be found after launching the
// app.
//
// This is prefered over launchWithName because localized builds have
// different names
launchWithManifestURL: function(manifestURL, entryPoint) {
GaiaApps.locateWithManifestURL(manifestURL, entryPoint, this.launch);
},
close: function(app, appName, entryPoint) {
if (app) {
let origin = GaiaApps.getRunningAppOrigin(appName);
GaiaApps.kill(origin);
} else {
marionetteScriptFinished(false);
}
},
// Closes app with the specified name (e.g., 'Calculator'); returns nothing
closeWithName: function(name) {
GaiaApps.locateWithName(name, this.close);
},
closeWithManifestURL: function(manifestURL, entryPoint) {
GaiaApps.locateWithManifestURL(manifestURL, entryPoint, this.close);
},
/**
* Returns the currently displayed app.
* This is more like getDisplayedFrame really,
* as we return what frame the user is interacting with
*/
getDisplayedApp: function() {
let app = window.wrappedJSObject.Service.query('getTopMostWindow');
if (!app || app.isLockscreen) {
// fallback to app window.
app = window.wrappedJSObject.Service
.query('AppWindowManager.getActiveWindow').getTopMostWindow();
}
let currentEntryPoint = null;
// When browser is on a website, app.manifest is null
if (app.manifest) {
let entryPoints = app.manifest.entry_points;
if (entryPoints) {
for (let ep in entryPoints) {
if (entryPoints[ep].launch_path == app.manifest.launch_path) {
currentEntryPoint = ep;
break;
}
}
}
}
let result = {
frame: (app.browser) ? app.browser.element : app.frame.firstChild,
src: (app.browser) ? app.browser.element.src : app.iframe.src,
name: app.name,
origin: app.origin,
manifestURL: app.manifestURL,
entryPoint: currentEntryPoint
};
return result;
},
/**
* Install the app with the specified ManifestURL
*/
install: function(manifestURL) {
let req = navigator.mozApps.install(manifestURL);
req.onsuccess = function() {
marionetteScriptFinished(true);
};
req.onerror = function() {
marionetteScriptFinished(req.error);
};
},
installPackage: function(manifestURL) {
let req = navigator.mozApps.installPackage(manifestURL);
req.onsuccess = function() {
marionetteScriptFinished(true);
};
req.onerror = function() {
marionetteScriptFinished(req.error);
};
},
/**
* Uninstall the app with the specified ManifestURL
*/
uninstall: function(manifestURL) {
GaiaApps.locateWithManifestURL(manifestURL, null, function uninstall(app) {
if (typeof(app) !== 'object') {
// App is already not there, so nothing to do here
marionetteScriptFinished(true);
return;
}
let req = navigator.mozApps.mgmt.uninstall(app);
req.onsuccess = function() {
marionetteScriptFinished(true);
};
req.onerror = function() {
marionetteScriptFinished(req.error);
};
});
}
};