summaryrefslogtreecommitdiff
path: root/static/js/web-install.js
diff options
context:
space:
mode:
Diffstat (limited to 'static/js/web-install.js')
-rw-r--r--static/js/web-install.js112
1 files changed, 99 insertions, 13 deletions
diff --git a/static/js/web-install.js b/static/js/web-install.js
index 7316e443..049800a6 100644
--- a/static/js/web-install.js
+++ b/static/js/web-install.js
@@ -7,7 +7,18 @@ const RELEASES_URL = "https://releases.grapheneos.org";
const CACHE_DB_NAME = "BlobStore";
const CACHE_DB_VERSION = 1;
-let safeToLeave = true;
+const Buttons = {
+ UNLOCK_BOOTLOADER: "unlock-bootloader",
+ DOWNLOAD_RELEASE: "download-release",
+ FLASH_RELEASE: "flash-release",
+ LOCK_BOOTLOADER: "lock-bootloader",
+ REMOVE_CUSTOM_KEY: "remove-custom-key"
+};
+
+const InstallerState = {
+ DOWNLOADING_RELEASE: 0x1,
+ INSTALLING_RELEASE: 0x2
+};
// This wraps XHR because getting progress updates with fetch() is overly complicated.
function fetchBlobWithProgress(url, onProgress) {
@@ -29,6 +40,12 @@ function fetchBlobWithProgress(url, onProgress) {
});
}
+function setButtonState({ id, enabled }) {
+ const button = document.getElementById(`${id}-button`);
+ button.disabled = !enabled;
+ return button;
+}
+
class BlobStore {
constructor() {
this.db = null;
@@ -106,8 +123,39 @@ class BlobStore {
}
}
+class ButtonController {
+ #map;
+
+ constructor() {
+ this.#map = new Map();
+ }
+
+ setEnabled(...ids) {
+ ids.forEach((id) => {
+ // Only enable button if it won't be disabled.
+ if (!this.#map.has(id)) {
+ this.#map.set(id, /* enabled = */ true);
+ }
+ });
+ }
+
+ setDisabled(...ids) {
+ ids.forEach((id) => this.#map.set(id, /* enabled = */ false));
+ }
+
+ applyState() {
+ this.#map.forEach((enabled, id) => {
+ setButtonState({ id, enabled });
+ });
+ this.#map.clear();
+ }
+}
+
+let installerState = 0;
+
let device = new fastboot.FastbootDevice();
let blobStore = new BlobStore();
+let buttonController = new ButtonController();
async function ensureConnected(setProgress) {
if (!device.isConnected) {
@@ -170,7 +218,7 @@ async function downloadRelease(setProgress) {
let [latestZip,] = await getLatestRelease();
// Download and cache the zip as a blob
- safeToLeave = false;
+ setInstallerState({ state: InstallerState.DOWNLOADING_RELEASE, active: true });
setProgress(`Downloading ${latestZip}...`);
await blobStore.init();
try {
@@ -178,7 +226,7 @@ async function downloadRelease(setProgress) {
setProgress(`Downloading ${latestZip}...`, progress);
});
} finally {
- safeToLeave = true;
+ setInstallerState({ state: InstallerState.DOWNLOADING_RELEASE, active: false });
}
setProgress(`Downloaded ${latestZip} release.`, 1.0);
}
@@ -225,7 +273,7 @@ async function flashRelease(setProgress) {
}
setProgress("Flashing release...");
- safeToLeave = false;
+ setInstallerState({ state: InstallerState.INSTALLING_RELEASE, active: true });
try {
await device.flashFactoryZip(blob, true, reconnectCallback,
(action, item, progress) => {
@@ -257,7 +305,7 @@ async function flashRelease(setProgress) {
await device.runCommand("erase:dpm_b");
}
} finally {
- safeToLeave = true;
+ setInstallerState({ state: InstallerState.INSTALLING_RELEASE, active: false });
}
return `Flashed ${latestZip} to device.`;
@@ -316,8 +364,7 @@ function addButtonHook(id, callback) {
}
};
- let button = document.getElementById(`${id}-button`);
- button.disabled = false;
+ let button = setButtonState({ id, enabled: true });
button.onclick = async () => {
try {
let finalStatus = await callback(statusCallback);
@@ -333,6 +380,45 @@ function addButtonHook(id, callback) {
};
}
+function setInstallerState({ state, active }) {
+ if (active) {
+ installerState |= state;
+ } else {
+ installerState &= ~state;
+ }
+ invalidateInstallerState();
+}
+
+function isInstallerStateActive(state) {
+ return (installerState & state) === state;
+}
+
+function invalidateInstallerState() {
+ if (isInstallerStateActive(InstallerState.DOWNLOADING_RELEASE)) {
+ buttonController.setDisabled(Buttons.DOWNLOAD_RELEASE);
+ } else {
+ buttonController.setEnabled(Buttons.DOWNLOAD_RELEASE);
+ }
+
+ let disableWhileInstalling = [
+ Buttons.DOWNLOAD_RELEASE,
+ Buttons.FLASH_RELEASE,
+ Buttons.LOCK_BOOTLOADER,
+ Buttons.REMOVE_CUSTOM_KEY,
+ ];
+ if (isInstallerStateActive(InstallerState.INSTALLING_RELEASE)) {
+ buttonController.setDisabled(...disableWhileInstalling);
+ } else {
+ buttonController.setEnabled(...disableWhileInstalling);
+ }
+
+ buttonController.applyState();
+}
+
+function safeToLeave() {
+ return installerState === 0;
+}
+
// This doesn't really hurt, and because this page is exclusively for web install,
// we can tolerate extra logging in the console in case something goes wrong.
fastboot.setDebugLevel(2);
@@ -344,18 +430,18 @@ fastboot.configureZip({
});
if ("usb" in navigator) {
- addButtonHook("unlock-bootloader", unlockBootloader);
- addButtonHook("download-release", downloadRelease);
- addButtonHook("flash-release", flashRelease);
- addButtonHook("lock-bootloader", lockBootloader);
- addButtonHook("remove-custom-key", eraseNonStockKey);
+ addButtonHook(Buttons.UNLOCK_BOOTLOADER, unlockBootloader);
+ addButtonHook(Buttons.DOWNLOAD_RELEASE, downloadRelease);
+ addButtonHook(Buttons.FLASH_RELEASE, flashRelease);
+ addButtonHook(Buttons.LOCK_BOOTLOADER, lockBootloader);
+ addButtonHook(Buttons.REMOVE_CUSTOM_KEY, eraseNonStockKey);
} else {
console.log("WebUSB unavailable");
}
// This will create an alert box to stop the user from leaving the page during actions
window.addEventListener("beforeunload", event => {
- if (!safeToLeave) {
+ if (!safeToLeave()) {
console.log("User tried to leave the page whilst unsafe to leave!");
event.returnValue = "";
}