Rewrite LightDMMock class except methods

Also rewrite loadJSON and checkForUpdate,
and exported the class the ES6 way.
This commit is contained in:
Elias Schriefer 2021-09-02 00:58:04 +02:00
parent b23543e7c1
commit c6f8b2da10

View File

@ -1,156 +1,344 @@
/**
* LightDMMock "class"
* @author Elias Schriefer <elischriefer@gmail.com>
* @alias lightdm
* @classdesc
* A LightDM Mock that is written in modern JavaScript and based on
* [cytodev](https://github.com/cytodev)'s
* [LightDMMock](https://github.com/cytodev/LightDMMock), the lightdm-webkit2-greeter
* [manual](https://man.archlinux.org/man/community/lightdm-webkit2-greeter/lightdm-webkit2-greeter.1.en),
* the [LightDM API documentation](https://people.ubuntu.com/~robert-ancell/lightdm/reference/),
* and the latest version of [Antergos](https://github.com/Antergos)'
* lightdm-webkit2-greeter [source code](https://github.com/Antergos/web-greeter).
* <font style="color:red">Please note that the deprecation errors are intrusive for a reason.</font>
*
* This is a rewrite in modern JavaScript.
*
* @author Roel Walraven <mail@cytodev.io>
* ##### Usage:
* 1. Set the type of your JavaScript to `module` in your theme that needs mocking
* ```html
* <script type="module" src="main.js"></script>
* ```
* ```html
* <script type="module">
* ...
* </script>
* ```
*
* 2. Import {@link LightDMMock} and create a new instance
* ```javascript
* import LightDMMock from "LightDMMock/src/LightDMMock.js";
*
* A LightDM Mock that is tightly based on the source C code of
* Antergos' lightdm-webkit2-greeter. Please note that the deprecation errors
* are intrusive for a reason.
*
* Usage:
* Include the file in your theme that needs mocking
* <script type="text/javascript" src="mock/LightDMMock.js"></script>
* Create a new instance of LightDMMock
* if(!("lightdm" in window)) {
* var LightDMMock = LightDMMock || {};
* window.lightdm = new LightDMMock(autofill, timeout, autoGuest);
* }
* If you want to use the .face images don't forget to add the path to
* LightDMMock/src to the image src. The users.json file has absolute paths
* like you would expect on a real filesystem.
*
* @param {boolean} autofill [wether or not the arrays for users, languages,
* layouts, and sessions need to be filled with mock
* data. I advise to test both to make your theme
* less prone to crashing.]
* @param {number} timeout [Value to use for simulated autologin (this value
* is in seconds).]
* @param {boolean} autoGuest [Wether or not to simulate automatic guest login.
* This will also enable a guest account
* in lightdm.has_guest_account]
* window.lightdm = new LightDMMock(autofill, timeout, autoGuest);
* ```
*
* If you want to use the `.face` images don't forget to add the path to
* `LightDMMock/src` to the image src. The `users.json` file has absolute paths
* like you would expect on a real filesystem.
*/
"use strict";
function LightDMMock(autofill, timeout, autoGuest) {
window.checkForUpdate("v1.1.0");
class LightDMMock {
// see <https://github.com/Antergos/web-greeter/blob/before-python/src/webkit2-extension.c#L1470-L1504>
// and <https://github.com/Antergos/web-greeter/blob/master/web-greeter/resources/js/docs/Greeter.js#L49-L240>
this.authentication_user = null;
this.autologin_guest = false;
this.autologin_timeout = 0;
this.autologin_user = null;
this.can_hibernate = false;
this.can_restart = false;
this.can_shutdown = false;
this.can_suspend = false;
this.default_session = null;
this.has_guest_account = false;
this.hide_users = false;
this.hostname = null;
this.in_authentication = false;
this.is_authenticated = false;
this.language = null;
this.languages = null;
this.layout = null;
this.layouts = null;
this.lock_hint = false;
this.num_users = 0;
this.select_guest_hint = null;
this.select_user_hint = null;
this.sessions = null;
this.users = null;
this.default_language = null; // Deprecated
this.default_layout = null; // Deprecated
this.select_guest = null; // Deprecated
this.select_user = null; // Deprecated
this.timed_login_delay = null; // Deprecated
this.timed_login_user = null; // Deprecated
/**
* @type {?String}
* @desc
* The username of the authentication user being authenticated
* or null if no authentication is in progress.
*/
authentication_user = null;
if(typeof autofill === "boolean" && autofill) {
var me = document.querySelector("script[src$=\"LightDMMock.js\"]");
/**
* @type {boolean}
* @desc
* Indicates the guest user should be used for autologin.
*/
autologin_guest = false;
if(!(me instanceof HTMLElement))
return window.console.error("Could not find my script element.");
/**
* @type {number}
* @desc
* The number of seconds to wait before automatically logging
* in. The older variable {@link lightdm.timed_user_delay} has
* been deprecated.
*/
autologin_timeout = 0;
var includePath = me.src;
/**
* @type {?String}
* @desc
* The name of the user account that should be logged into
* automatically after timed login delay has passed. The older
* variable {@link lightdm.timed_login_user} has been deprecated.
*/
autologin_user = null;
if(includePath === undefined)
return window.console.error("Could not find my src attribute.");
/**
* @type {boolean}
* @desc
* Whether or not the system can be made to hibernate by the
* greeter.
*/
can_hibernate = false;
includePath = includePath.substr(0, includePath.lastIndexOf("/"));
/**
* @type {boolean}
* @desc
* Whether or not the system can be restarted by the greeter.
*/
can_restart = false;
var asyncLoadEnd = function(that) {
that.default_session = that.sessions[0].name;
that.language = that.languages[0].name;
that.layout = that.layouts[0].name;
that.num_users = that.users.length;
/**
* @type {boolean}
* @desc
* Whether or not the system can be shutdown by the greeter.
*/
can_shutdown = false;
if(typeof timeout === "number" && timeout > 0) {
if(typeof autoGuest === "boolean" && autoGuest) {
that.autologin_user = null;
that.autologin_guest = autoGuest;
}
/**
* @type {boolean}
* @desc
* Whether or not the system can be suspended by the greeter.
*/
can_suspend = false;
that.autologin_user = that.users[0].username;
that.autologin_timeout = timeout * 1000;
/**
* @deprecated
*/
default_language = null;
// @fixme: am I deprecated as well?
setTimeout(function() {
if((typeof autoGuest === "boolean" && autoGuest) || that.autologin_user !== null)
window.autologin_timer_expired();
}.bind(that), that.timed_login_delay);
/**
* @deprecated
*/
default_layout = null;
/**
* @type {?String}
* @desc
* The name of the default session (as configured in
* `lightdm.conf`).
*/
default_session = null;
/**
* @type {boolean}
* @desc
* A guest account is available for login.
*/
has_guest_account = false;
/**
* @type {boolean}
* @desc
* The whole list of users should not be displayed.
*/
hide_users = false;
/**
* @type {String}
* @desc
* The hostname of the system.
*/
hostname = null;
/**
* @type {boolean}
* @desc
* Indicates if the user has successfully authenticated.
*/
is_authenticated = false;
/**
* @type {boolean}
* @desc
* Indicates if lightdm is currently in the authentication
* phase.
*/
in_authentication = false;
/**
* @type {?String}
* @desc
* The currently selected language. The older variable name
* {@link lightdm.default_language} is deprecated.
*/
language = null;
/**
* @type {LightDMLanguage[]}
* @desc
* The languages that are available on the system.
*/
languages = null;
/**
* @type {String}
* @desc
* The name of the currently active keyboard layout. To change
* the layout, assign a valid layout name to this variable. The older
* variable name {@link lightdm.default_layout} is deprecated.
*/
layout = null;
/**
* @type {LightDMLayout[]}
* @desc
* The keyboard layouts that are available on the system.
*/
layouts = null;
/**
* @type {boolean}
* @desc
* `true` if the greeter was triggered by locking the seat.
*/
lock_hint = false;
/**
* @type {number}
* @desc
* The number of users able to log in.
*/
num_users = 0;
/**
* @deprecated
* @type {boolean}
* @desc
* The guest user should be selected by default for login.
*/
select_guest = null;
/**
* @type {boolean}
* @desc
* `true` if the guest account should be selected by default.
*/
select_guest_hint = false;
/**
* @deprecated
* @type {?String}
* @desc
* The username that should be selected by default for login.
*/
select_user = null;
/**
* @type {?String}
* @desc
* A username or `null` if no particular user should be selected.
*/
select_user_hint = null;
/**
* @type {LightDMSession[]}
* @desc
* The sessions that are available on the system.
*/
sessions = null;
/**
* @deprecated
*/
timed_login_delay = null;
/**
* @deprecated
*/
timed_login_user = null;
/**
* @type {LightDMUser[]}
* @desc
* The users that are able to log in.
*/
users = null;
/**
* @param {boolean} [autofill=false]
* Whether or not the arrays for users, languages, layouts, and sessions need to be filled with mock
* data. I advise to test both to make your theme less prone to crashing.
*
* @param {number} [timeout=0]
* Value to use for simulated autologin (this value is in seconds).
*
* @param {boolean} [autoGuest=false]
* Whether or not to simulate automatic guest login. This will also enable a guest account in
* {@link LightDMMock.has_guest_account lightdm.has_guest_account}
*/
constructor(autofill = false, timeout = 0, autoGuest = false) {
checkForUpdate("v1.1.0");
if (typeof autofill == "boolean" && autofill) {
let includePath = import.meta.url;
if (includePath == undefined) {
return console.error("Could not get module URL.");
}
for(var i = 0; i <= that.users; i++) {
that.users[i].logged_in = Boolean(Math.floor(Math.random() * 2));
that.users[i].session = that.sessions[Math.floor((Math.random() * that.sessions.length))].name;
}
};
includePath = includePath.substr(0, includePath.lastIndexOf("/"));
// see <https://github.com/Antergos/web-greeter/blob/before-python/src/webkit2-extension.c#L1437-L1450>
window.loadJSON(includePath + "/json/users.json", function(that) {
if(this.status !== 200)
return window.console.warn("users.json did not load correctly.");
const rejectionHandler = response => {
console.warn(`${response.url.substr(response.url.lastIndexOf("/") + 1)} did not load correctly.`);
};
that.users = JSON.parse(this.responseText);
// see <https://github.com/Antergos/web-greeter/blob/master/web-greeter/resources/js/docs/LightDMObjects.js#L169-L253>
const usersJSON = loadJSON(`${includePath}/json/users.json`);
usersJSON
.then(json => {
this.users = json;
}, rejectionHandler)
if(that.users !== null && that.languages !== null && that.layouts !== null && that.sessions !== null)
asyncLoadEnd(that);
}, this, asyncLoadEnd);
// see <https://github.com/Antergos/web-greeter/blob/master/web-greeter/resources/js/docs/LightDMObjects.js#L85-L124>
const languagesJSON = loadJSON(`${includePath}/json/languages.json`);
languagesJSON
.then(json => {
this.languages = json;
}, rejectionHandler);
// see <https://github.com/Antergos/web-greeter/blob/before-python/src/webkit2-extension.c#L1452-L1456>
window.loadJSON(includePath + "/json/languages.json", function(that) {
if(this.status !== 200)
return window.console.warn("languages.json did not load correctly.");
// see <https://github.com/Antergos/web-greeter/blob/master/web-greeter/resources/js/docs/LightDMObjects.js#L127-L166>
const layoutsJSON = loadJSON(`${includePath}/json/layouts.json`);
layoutsJSON
.then(json => {
this.layouts = json;
}, rejectionHandler);
that.languages = JSON.parse(this.responseText);
// see <https://github.com/Antergos/web-greeter/blob/master/web-greeter/resources/js/docs/LightDMObjects.js#L43-L82>
const sessionsJSON = loadJSON(`${includePath}/json/sessions.json`);
sessionsJSON
.then(json => {
this.sessions = json;
}, rejectionHandler);
if(that.users !== null && that.languages !== null && that.layouts !== null && that.sessions !== null)
asyncLoadEnd(that);
}, this, asyncLoadEnd);
Promise.all([usersJSON, languagesJSON, layoutsJSON, sessionsJSON])
.then(() => {
this.default_session = this.sessions[0].name;
this.language = this.languages[0].name;
this.layout = this.layouts[0].name;
this.num_users = this.users.length;
// see <https://github.com/Antergos/web-greeter/blob/before-python/src/webkit2-extension.c#L1458-L1462>
window.loadJSON(includePath + "/json/layouts.json", function(that) {
if(this.status !== 200)
return window.console.warn("layouts.json did not load correctly.");
if (typeof timeout == "number" && timeout > 0) {
if (typeof autoGuest == "boolean" && autoGuest) {
this.autologin_user = null;
this.autologin_guest = autoGuest;
} else {
this.autologin_user = this.users[0].username;
}
that.layouts = JSON.parse(this.responseText);
this.autologin_timeout = timeout * 1000;
if(that.users !== null && that.languages !== null && that.layouts !== null && that.sessions !== null)
asyncLoadEnd(that);
}, this, asyncLoadEnd);
setTimeout(() => {
if ((typeof autoGuest == "boolean" && autoGuest) || this.autologin_user != null)
window.autologin_timer_expired();
}, this.autologin_timeout);
}
// see <https://github.com/Antergos/web-greeter/blob/before-python/src/webkit2-extension.c#L1464-L1468>
window.loadJSON(includePath + "/json/sessions.json", function(that) {
if(this.status !== 200)
return window.console.warn("sessions.json did not load correctly.");
that.sessions = JSON.parse(this.responseText);
if(that.users !== null && that.languages !== null && that.layouts !== null && that.sessions !== null)
asyncLoadEnd(that);
}, this, asyncLoadEnd);
for (let user of this.users) {
user.logged_in = Boolean(Math.floor(Math.random() * 2));
user.session = this.sessions[Math.floor((Math.random() * this.sessions.length))].name;
}
});
}
}
}
@ -525,72 +713,110 @@ window.logCall = function(name, args) {
};
/**
* global helper checkForUpdate
* compares curentVersion with the tag name of GitHub's latest release and
* prompts the user to download a new version if it is available.
* @desc
* Compares `curentVersion` with the tag name of Gitea's latest release and
* prompts the user to download a new version if it is available.
*
* @param {String} currentVersion [the current tag version]
* @param {String} currentVersion
* the current tag version
*/
window.checkForUpdate = function(currentVersion) {
var request = new XMLHttpRequest();
export const checkForUpdate = async currentVersion => {
const errMessage = "Could not check for new version of LightDMMock. Please check for a new version manually by visiting https://git.sfs.ddnss.org/EliasSchriefer/LightDMMock/releases/latest";
const response = await fetch("https://git.sfs.ddnss.org/api/v1/repos/EliasSchriefer/LightDMMock/releases");
request.onreadystatechange = function() {
if(request.readyState === XMLHttpRequest.DONE) {
switch(request.status) {
case 200:
try {
var latest;
if (response.ok) {
try {
const request = (await request.json())?.[0]?.tag_name;
if(request.responseText !== undefined)
latest = JSON.parse(request.responseText)[0].tag_name;
if(currentVersion !== latest)
window.console.warn("You are using an outdated version of LightDMMock. Please download the new version from https://git.sfs.ddnss.org/EliasSchriefer/LightDMMock/releases/tag/" + latest);
} catch(e) {
window.console.error(e.toString());
window.console.warn("Could not check for new version of LightDMMock. Please check for a new version manually by visiting https://git.sfs.ddnss.org/EliasSchriefer/LightDMMock/releases/latest");
}
break;
case 404:
window.console.warn("Could not check for new version of LightDMMock. Please check for a new version manually by visiting https://git.sfs.ddnss.org/EliasSchriefer/LightDMMock/releases/latest");
break;
default:
break;
if (currentVersion != latest) {
console.warn(`You are using an outdated version of LightDMMock. Please download the new version from https://git.sfs.ddnss.org/EliasSchriefer/LightDMMock/releases/tag/${latest}`);
}
} catch (err) {
console.error(err.toString());
console.warn(errMessage);
}
};
request.open("GET", "https://git.sfs.ddnss.org/api/v1/repos/EliasSchriefer/LightDMMock/releases", true);
request.send();
} else if (response.status == 404) {
console.warn(errMessage);
}
};
/**
* global helper loadJSON
* Loads JSON from a path. Removes the need to b64 encode them in this file.
* @desc
* Loads JSON from a path. Removes the need to b64 encode them in this file.
*
* @param {String} url [path to JSON file]
* @param {Function} callback [callback function]
* @param {String} url
* path to JSON file
*
* @throws {Response} Response with status code other than 200 Ok
*/
window.loadJSON = function(url, callback) {
var request = new XMLHttpRequest();
export const loadJSON = async url => {
let response = await fetch(url);
var onSuccess = function() {
this.callback.apply(request, request.arguments);
};
var onFailure = function() {
window.console.error(this.statusText);
};
request.callback = callback;
request.arguments = Array.prototype.slice.call(arguments, 2);
request.onload = onSuccess;
request.onerror = onFailure;
request.open("get", url, true);
request.send(null);
if (response.ok) {
return await response.json();
} else {
console.error(response.statusText);
throw response;
}
};
/******************************************************************************
* External global functions *
******************************************************************************/
/**
* @namespace window
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/window}
*/
/**
* @function show_prompt
* @memberof window
* @desc
* This will be called when LightDM needs to prompt the user for some
* reason, such as asking for a password.
*
* @param {String} text
* Will be the text of the prompt
*
* @param {String} type
* Will either be
* - "text" for a visible prompt, or
* - "password" for a prompt that the input should be hidden.
*/
/**
* @function show_message
* @memberof window
* @desc
* This will be called when LightDM needs to display some info for the
* user.
*
* @param {String} text
* Will be the text of the message
*
* @param {String} type
* Will either be
* - "info" for an information message, or
* - "error" for an error message that LightDM has encountered.
*/
/**
* @function authentication_complete
* @memberof window
* @desc
* This function is called by LightDM when authentication has
* completed.
*/
/**
* @function autologin_timer_expired
* @memberof window
* @desc
* This function is called by LightDM when an autologin user's login
* timer has expired. The greeter should reset the authentication
* process.
*/
/******************************************************************************
* Object.watch shim *
@ -684,9 +910,4 @@ LightDMMock.watch('timed_login_delay', function() {
* Module loading *
******************************************************************************/
/* jshint node : true */
if(typeof module !== "undefined" && module.exports)
module.exports = LightDMMock;
/* jshint node : false */
export default LightDMMock;