/* 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/. */ export var CrashReports = { pendingDir: null, reportsDir: null, submittedDir: null, getReports: function CrashReports_getReports() { let reports = {}; let ignored = []; try { // Ignore any non http/https urls if (!/^https?:/i.test(Services.prefs.getCharPref("breakpad.reportURL"))) { return []; } } catch (e) {} if (this.submittedDir.exists() && this.submittedDir.isDirectory()) { let entries = this.submittedDir.directoryEntries; while (entries.hasMoreElements()) { let file = entries.nextFile; let leaf = file.leafName; if (leaf.startsWith("bp-") && leaf.endsWith(".txt")) { let date = lastModifiedOrNull(file); if (date === null) { continue; } let entry = { id: leaf.slice(0, -4), date, pending: false, ignored: false, }; reports[entry.id] = entry; } } } if (this.pendingDir.exists() && this.pendingDir.isDirectory()) { let uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; let entries = this.pendingDir.directoryEntries; while (entries.hasMoreElements()) { let file = entries.nextFile; let leaf = file.leafName; let id = leaf.slice(0, 36); let extension = leaf.slice(36); if (!uuidRegex.test(id)) { continue; } if (extension === ".dmp") { let date = lastModifiedOrNull(file); if (date === null) { continue; } let entry = { id, date, pending: true, ignored: false, }; reports[id] = entry; } else if (extension === ".dmp.ignore") { ignored.push(id); } } } for (let id of ignored) { let report = reports[id]; if (report) { report.ignored = true; } } // Sort reports descending by date return Object.values(reports).sort((a, b) => b.date - a.date); }, /** * Deletes `InstallTime` marker files from the crash reports * directory whose last-modified time is older than `maxAgeMs`. Tolerates * a missing crash reports directory (e.g. on a fresh profile). * * @param {number} maxAgeMs Maximum age in milliseconds; files older than * this (relative to `Date.now()`) are removed. */ async pruneInstallTimeFiles(maxAgeMs) { const threshold = Date.now() - maxAgeMs; let children; try { children = await IOUtils.getChildren(this.reportsDir.path); } catch (e) { if (!DOMException.isInstance(e) || e.name !== "NotFoundError") { throw e; } return; } for (const childPath of children) { if (!PathUtils.filename(childPath).startsWith("InstallTime")) { continue; } const stat = await IOUtils.stat(childPath); if (stat.lastModified < threshold) { await IOUtils.remove(childPath); } } }, /** * Deletes all files belonging to a single pending crash report: * the minidump (`.dmp`), the annotations file (`.extra`), the optional * ignore marker (`.dmp.ignore`), and the optional memory report * (`.memory.json.gz`). Missing files are tolerated. * * @param {string} id The crash report's UUID (without any file extension). */ async deletePendingReport(id) { const base = PathUtils.join(this.pendingDir.path, id); for (const suffix of [".dmp", ".extra", ".dmp.ignore", ".memory.json.gz"]) { await IOUtils.remove(base + suffix, { ignoreAbsent: true }); } }, /** * Deletes the `bp-.txt` record of a successfully submitted crash * report. Missing file is tolerated. * * @param {string} id The submitted report identifier as it appears in * `bp-.txt` (i.e. the full `bp-` string * without the `.txt` extension). */ async deleteSubmittedReport(id) { await IOUtils.remove(PathUtils.join(this.submittedDir.path, id + ".txt"), { ignoreAbsent: true, }); }, }; /** * Returns the last-modified time of `file`, or null if it no longer exists. */ function lastModifiedOrNull(file) { try { return file.lastModifiedTime; } catch (e) { if (e.result === Cr.NS_ERROR_FILE_NOT_FOUND) { return null; } throw e; } } function CrashReports_pendingDir() { let pendingDir = Services.dirsvc.get("UAppData", Ci.nsIFile); pendingDir.append("Crash Reports"); pendingDir.append("pending"); return pendingDir; } function CrashReports_reportsDir() { let reportsDir = Services.dirsvc.get("UAppData", Ci.nsIFile); reportsDir.append("Crash Reports"); return reportsDir; } function CrashReports_submittedDir() { let submittedDir = Services.dirsvc.get("UAppData", Ci.nsIFile); submittedDir.append("Crash Reports"); submittedDir.append("submitted"); return submittedDir; } CrashReports.pendingDir = CrashReports_pendingDir(); CrashReports.reportsDir = CrashReports_reportsDir(); CrashReports.submittedDir = CrashReports_submittedDir();