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 * window.lightdm = new LightDMMock(autofill, timeout, autoGuest);
* Antergos' lightdm-webkit2-greeter. Please note that the deprecation errors * ```
* are intrusive for a reason. *
* * If you want to use the `.face` images don't forget to add the path to
* Usage: * `LightDMMock/src` to the image src. The `users.json` file has absolute paths
* Include the file in your theme that needs mocking * like you would expect on a real filesystem.
* <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]
*/ */
"use strict"; class LightDMMock {
function LightDMMock(autofill, timeout, autoGuest) {
window.checkForUpdate("v1.1.0");
// see <https://github.com/Antergos/web-greeter/blob/before-python/src/webkit2-extension.c#L1470-L1504> // 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; * @type {?String}
this.autologin_timeout = 0; * @desc
this.autologin_user = null; * The username of the authentication user being authenticated
this.can_hibernate = false; * or null if no authentication is in progress.
this.can_restart = false; */
this.can_shutdown = false; authentication_user = null;
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
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; * @type {boolean}
that.language = that.languages[0].name; * @desc
that.layout = that.layouts[0].name; * Whether or not the system can be shutdown by the greeter.
that.num_users = that.users.length; */
can_shutdown = false;
if(typeof timeout === "number" && timeout > 0) { /**
if(typeof autoGuest === "boolean" && autoGuest) { * @type {boolean}
that.autologin_user = null; * @desc
that.autologin_guest = autoGuest; * 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() { * @deprecated
if((typeof autoGuest === "boolean" && autoGuest) || that.autologin_user !== null) */
window.autologin_timer_expired(); default_layout = null;
}.bind(that), that.timed_login_delay);
/**
* @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++) { includePath = includePath.substr(0, includePath.lastIndexOf("/"));
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;
}
};
// see <https://github.com/Antergos/web-greeter/blob/before-python/src/webkit2-extension.c#L1437-L1450> const rejectionHandler = response => {
window.loadJSON(includePath + "/json/users.json", function(that) { console.warn(`${response.url.substr(response.url.lastIndexOf("/") + 1)} did not load correctly.`);
if(this.status !== 200) };
return window.console.warn("users.json 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) // see <https://github.com/Antergos/web-greeter/blob/master/web-greeter/resources/js/docs/LightDMObjects.js#L85-L124>
asyncLoadEnd(that); const languagesJSON = loadJSON(`${includePath}/json/languages.json`);
}, this, asyncLoadEnd); languagesJSON
.then(json => {
this.languages = json;
}, rejectionHandler);
// see <https://github.com/Antergos/web-greeter/blob/before-python/src/webkit2-extension.c#L1452-L1456> // see <https://github.com/Antergos/web-greeter/blob/master/web-greeter/resources/js/docs/LightDMObjects.js#L127-L166>
window.loadJSON(includePath + "/json/languages.json", function(that) { const layoutsJSON = loadJSON(`${includePath}/json/layouts.json`);
if(this.status !== 200) layoutsJSON
return window.console.warn("languages.json did not load correctly."); .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) Promise.all([usersJSON, languagesJSON, layoutsJSON, sessionsJSON])
asyncLoadEnd(that); .then(() => {
}, this, asyncLoadEnd); 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> if (typeof timeout == "number" && timeout > 0) {
window.loadJSON(includePath + "/json/layouts.json", function(that) { if (typeof autoGuest == "boolean" && autoGuest) {
if(this.status !== 200) this.autologin_user = null;
return window.console.warn("layouts.json did not load correctly."); 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) setTimeout(() => {
asyncLoadEnd(that); if ((typeof autoGuest == "boolean" && autoGuest) || this.autologin_user != null)
}, this, asyncLoadEnd); window.autologin_timer_expired();
}, this.autologin_timeout);
}
// see <https://github.com/Antergos/web-greeter/blob/before-python/src/webkit2-extension.c#L1464-L1468> for (let user of this.users) {
window.loadJSON(includePath + "/json/sessions.json", function(that) { user.logged_in = Boolean(Math.floor(Math.random() * 2));
if(this.status !== 200) user.session = this.sessions[Math.floor((Math.random() * this.sessions.length))].name;
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);
} }
} }
@ -525,72 +713,110 @@ window.logCall = function(name, args) {
}; };
/** /**
* global helper checkForUpdate * @desc
* compares curentVersion with the tag name of GitHub's latest release and * Compares `curentVersion` with the tag name of Gitea's latest release and
* prompts the user to download a new version if it is available. * 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) { export const checkForUpdate = async currentVersion => {
var request = new XMLHttpRequest(); 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 (response.ok) {
if(request.readyState === XMLHttpRequest.DONE) { try {
switch(request.status) { const request = (await request.json())?.[0]?.tag_name;
case 200:
try {
var latest;
if(request.responseText !== undefined) if (currentVersion != latest) {
latest = JSON.parse(request.responseText)[0].tag_name; 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}`);
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;
} }
} catch (err) {
console.error(err.toString());
console.warn(errMessage);
} }
}; } else if (response.status == 404) {
console.warn(errMessage);
request.open("GET", "https://git.sfs.ddnss.org/api/v1/repos/EliasSchriefer/LightDMMock/releases", true); }
request.send();
}; };
/** /**
* global helper loadJSON * @desc
* Loads JSON from a path. Removes the need to b64 encode them in this file. * Loads JSON from a path. Removes the need to b64 encode them in this file.
* *
* @param {String} url [path to JSON file] * @param {String} url
* @param {Function} callback [callback function] * path to JSON file
*
* @throws {Response} Response with status code other than 200 Ok
*/ */
window.loadJSON = function(url, callback) { export const loadJSON = async url => {
var request = new XMLHttpRequest(); let response = await fetch(url);
var onSuccess = function() { if (response.ok) {
this.callback.apply(request, request.arguments); return await response.json();
}; } else {
console.error(response.statusText);
var onFailure = function() { throw response;
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);
}; };
/******************************************************************************
* 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 * * Object.watch shim *
@ -684,9 +910,4 @@ LightDMMock.watch('timed_login_delay', function() {
* Module loading * * Module loading *
******************************************************************************/ ******************************************************************************/
/* jshint node : true */ export default LightDMMock;
if(typeof module !== "undefined" && module.exports)
module.exports = LightDMMock;
/* jshint node : false */