tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

debug.js (8985B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 /* import-globals-from /toolkit/content/preferencesBindings.js */
      6 
      7 Preferences.addAll([
      8  { id: "browser.backup.enabled", type: "bool" },
      9  { id: "browser.backup.log", type: "bool" },
     10 ]);
     11 
     12 const { BackupService } = ChromeUtils.importESModule(
     13  "resource:///modules/backup/BackupService.sys.mjs"
     14 );
     15 
     16 let DebugUI = {
     17  init() {
     18    let controls = document.querySelector("#controls");
     19    controls.addEventListener("click", this);
     20 
     21    let encryptionEnabled = document.querySelector("#encryption-enabled");
     22    encryptionEnabled.addEventListener("click", this);
     23 
     24    // We use `init` instead of `get` here, since this page might load before
     25    // the BackupService has had a chance to initialize itself.
     26    let service = BackupService.init();
     27    service.addEventListener("BackupService:StateUpdate", this);
     28    this.onStateUpdate();
     29 
     30    // Kick-off reading any pre-existing encryption state off of the disk.
     31    service.loadEncryptionState();
     32  },
     33 
     34  handleEvent(event) {
     35    switch (event.type) {
     36      case "BackupService:StateUpdate": {
     37        this.onStateUpdate();
     38        break;
     39      }
     40      case "click": {
     41        let target = event.target;
     42        if (HTMLButtonElement.isInstance(event.target)) {
     43          this.onButtonClick(target);
     44        } else if (
     45          HTMLInputElement.isInstance(event.target) &&
     46          event.target.type == "checkbox"
     47        ) {
     48          event.preventDefault();
     49          this.onCheckboxClick(target);
     50        }
     51        break;
     52      }
     53    }
     54  },
     55 
     56  secondsToHms(seconds) {
     57    let h = Math.floor(seconds / 3600);
     58    let m = Math.floor((seconds % 3600) / 60);
     59    let s = Math.floor((seconds % 3600) % 60);
     60    return `${h}h ${m}m ${s}s`;
     61  },
     62 
     63  async onButtonClick(button) {
     64    switch (button.id) {
     65      case "create-backup": {
     66        let service = BackupService.get();
     67        let lastBackupStatus = document.querySelector("#last-backup-status");
     68        lastBackupStatus.textContent = "Creating backup...";
     69 
     70        let then = ChromeUtils.now();
     71        button.disabled = true;
     72        await service.createBackup();
     73        let totalTimeSeconds = (ChromeUtils.now() - then) / 1000;
     74        button.disabled = false;
     75        new Notification(`Backup created`, {
     76          body: `Total time ${this.secondsToHms(totalTimeSeconds)}`,
     77        });
     78        lastBackupStatus.textContent = `Backup created - total time: ${this.secondsToHms(
     79          totalTimeSeconds
     80        )}`;
     81        break;
     82      }
     83      case "open-backup-folder": {
     84        let backupsDir = PathUtils.join(
     85          PathUtils.profileDir,
     86          BackupService.PROFILE_FOLDER_NAME
     87        );
     88 
     89        let nsLocalFile = Components.Constructor(
     90          "@mozilla.org/file/local;1",
     91          "nsIFile",
     92          "initWithPath"
     93        );
     94 
     95        if (await IOUtils.exists(backupsDir)) {
     96          new nsLocalFile(backupsDir).reveal();
     97        } else {
     98          alert("backups folder doesn't exist yet");
     99        }
    100 
    101        break;
    102      }
    103      case "recover-from-staging": {
    104        let backupsDir = PathUtils.join(
    105          PathUtils.profileDir,
    106          BackupService.PROFILE_FOLDER_NAME
    107        );
    108        let fp = Cc["@mozilla.org/filepicker;1"].createInstance(
    109          Ci.nsIFilePicker
    110        );
    111        fp.init(
    112          window.browsingContext,
    113          "Choose a staging folder",
    114          Ci.nsIFilePicker.modeGetFolder
    115        );
    116        fp.displayDirectory = await IOUtils.getDirectory(backupsDir);
    117        let result = await new Promise(resolve => fp.open(resolve));
    118        if (result == Ci.nsIFilePicker.returnCancel) {
    119          break;
    120        }
    121 
    122        let path = fp.file.path;
    123        let lastRecoveryStatus = document.querySelector(
    124          "#last-recovery-status"
    125        );
    126        lastRecoveryStatus.textContent = "Recovering from backup...";
    127 
    128        let service = BackupService.get();
    129        try {
    130          let newProfile = await service.recoverFromSnapshotFolder(
    131            path,
    132            true /* shouldLaunch */
    133          );
    134          lastRecoveryStatus.textContent = `Created profile ${newProfile.name} at ${newProfile.rootDir.path}`;
    135        } catch (e) {
    136          lastRecoveryStatus.textContent(
    137            `Failed to recover: ${e.message} Check the console for the full exception.`
    138          );
    139          throw e;
    140        }
    141        break;
    142      }
    143      case "extract-from-archive": {
    144        let backupsDir = PathUtils.join(
    145          PathUtils.profileDir,
    146          BackupService.PROFILE_FOLDER_NAME
    147        );
    148        let fp = Cc["@mozilla.org/filepicker;1"].createInstance(
    149          Ci.nsIFilePicker
    150        );
    151        fp.init(
    152          window.browsingContext,
    153          "Choose an archive file",
    154          Ci.nsIFilePicker.modeOpen
    155        );
    156        fp.displayDirectory = await IOUtils.getDirectory(backupsDir);
    157        let result = await new Promise(resolve => fp.open(resolve));
    158        if (result == Ci.nsIFilePicker.returnCancel) {
    159          break;
    160        }
    161 
    162        let extractionStatus = document.querySelector("#extraction-status");
    163        extractionStatus.textContent = "Extracting...";
    164 
    165        let path = fp.file.path;
    166        let dest = PathUtils.join(PathUtils.parent(path), "extraction.zip");
    167        let service = BackupService.get();
    168        try {
    169          let { isEncrypted } = await service.sampleArchive(path);
    170          let recoveryCode = undefined;
    171          if (isEncrypted) {
    172            recoveryCode = prompt("Please provide the decryption password");
    173          }
    174          await service.extractCompressedSnapshotFromArchive(
    175            path,
    176            dest,
    177            recoveryCode
    178          );
    179          extractionStatus.textContent = `Extracted ZIP file to ${dest}`;
    180        } catch (e) {
    181          extractionStatus.textContent = `Failed to extract: ${e.message} Check the console for the full exception.`;
    182          throw e;
    183        }
    184        break;
    185      }
    186      case "recover-from-archive": {
    187        let backupsDir = PathUtils.join(
    188          PathUtils.profileDir,
    189          BackupService.PROFILE_FOLDER_NAME
    190        );
    191        let fp = Cc["@mozilla.org/filepicker;1"].createInstance(
    192          Ci.nsIFilePicker
    193        );
    194        fp.init(
    195          window.browsingContext,
    196          "Choose an archive file",
    197          Ci.nsIFilePicker.modeOpen
    198        );
    199        fp.displayDirectory = await IOUtils.getDirectory(backupsDir);
    200        fp.appendFilters(Ci.nsIFilePicker.filterHTML);
    201 
    202        let result = await new Promise(resolve => fp.open(resolve));
    203        if (result == Ci.nsIFilePicker.returnCancel) {
    204          break;
    205        }
    206 
    207        let recoverFromArchiveStatus = document.querySelector(
    208          "#recover-from-archive-status"
    209        );
    210        recoverFromArchiveStatus.textContent =
    211          "Recovering from backup archive...";
    212 
    213        let path = fp.file.path;
    214        let service = BackupService.get();
    215        try {
    216          let { isEncrypted } = await service.sampleArchive(path);
    217          let recoveryCode = undefined;
    218          if (isEncrypted) {
    219            recoveryCode = prompt("Please provide the decryption password");
    220          }
    221          let newProfile = await service.recoverFromBackupArchive(
    222            path,
    223            recoveryCode,
    224            true /* shouldLaunch */
    225          );
    226          recoverFromArchiveStatus.textContent = `Created profile ${newProfile.name} at ${newProfile.rootDir.path}`;
    227        } catch (e) {
    228          recoverFromArchiveStatus.textContent = `Failed to recover: ${e.message} Check the console for the full exception.`;
    229          throw e;
    230        }
    231        break;
    232      }
    233    }
    234  },
    235 
    236  async onCheckboxClick(checkbox) {
    237    if (checkbox.id == "encryption-enabled") {
    238      let service = BackupService.get();
    239      if (checkbox.checked) {
    240        let password = prompt("What's the encryption password? (8 char min)");
    241        if (password != null) {
    242          try {
    243            await service.enableEncryption(password);
    244          } catch (e) {
    245            console.error(e);
    246          }
    247        }
    248      } else if (confirm("Disable encryption?")) {
    249        try {
    250          await service.disableEncryption();
    251        } catch (e) {
    252          console.error(e);
    253        }
    254      }
    255    }
    256  },
    257 
    258  onStateUpdate() {
    259    let service = BackupService.get();
    260    let state = service.state;
    261 
    262    let encryptionEnabled = document.querySelector("#encryption-enabled");
    263    encryptionEnabled.checked = state.encryptionEnabled;
    264  },
    265 };
    266 
    267 // Wait until the load event fires before setting up any listeners or updating
    268 // any of the state of the page. We do this in order to avoid having any of
    269 // our control states overwritten by SessionStore after a restoration, as
    270 // restoration of form state occurs _prior_ to the load event firing.
    271 addEventListener(
    272  "load",
    273  () => {
    274    DebugUI.init();
    275  },
    276  { once: true }
    277 );