137 lines
3.9 KiB
JavaScript
137 lines
3.9 KiB
JavaScript
/* 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/. */
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
CrashSubmit: "resource://gre/modules/CrashSubmit.sys.mjs",
|
|
CrashServiceUtils: "resource://gre/modules/CrashService.sys.mjs",
|
|
RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
|
|
RemoteSettingsClient:
|
|
"resource://services-settings/RemoteSettingsClient.sys.mjs",
|
|
});
|
|
|
|
const REMOTE_SETTINGS_CRASH_COLLECTION = "crash-reports-ondemand";
|
|
|
|
// Remote Settings collections might want a different limit
|
|
const PENDING_REMOTE_CRASH_REPORT_DAYS = 90;
|
|
|
|
export var RemoteSettingsCrashPull = {
|
|
_initialized: false,
|
|
_client: undefined,
|
|
_showCallback: undefined,
|
|
_remoteSettingsCallback: undefined,
|
|
|
|
async start(aCallback) {
|
|
if (this._initialized) {
|
|
return;
|
|
}
|
|
|
|
const enabled = Services.prefs.getBoolPref(
|
|
"browser.crashReports.crashPull"
|
|
);
|
|
if (!enabled) {
|
|
return;
|
|
}
|
|
|
|
this._showCallback = aCallback;
|
|
|
|
await this.collection();
|
|
|
|
this._initialized = true;
|
|
},
|
|
|
|
stop() {
|
|
if (!this._initialized) {
|
|
return;
|
|
}
|
|
|
|
this._showCallback = undefined;
|
|
if (this._remoteSettingsCallback) {
|
|
this._client.off("sync", this._remoteSettingsCallback);
|
|
this._remoteSettingsCallback = undefined;
|
|
}
|
|
this._initialized = false;
|
|
},
|
|
|
|
async remoteSettingsCallback({ data: { created } }) {
|
|
await this.checkInterestingCrashesAndMaybeNotify(created);
|
|
},
|
|
|
|
/**
|
|
* Setup RemoteSettings listeners
|
|
*/
|
|
async collection() {
|
|
// Bind once so we can on("sync") then off("sync") with the same callback
|
|
this._remoteSettingsCallback = this.remoteSettingsCallback.bind(this);
|
|
|
|
try {
|
|
this._client = lazy.RemoteSettings(REMOTE_SETTINGS_CRASH_COLLECTION);
|
|
this._client.on("sync", this._remoteSettingsCallback);
|
|
|
|
let data = await this._client.get();
|
|
await this.checkInterestingCrashesAndMaybeNotify(data);
|
|
} catch (ex) {
|
|
if (!(ex instanceof lazy.RemoteSettingsClient.UnknownCollectionError)) {
|
|
throw ex;
|
|
}
|
|
}
|
|
},
|
|
|
|
async getPendingSha256(dateLimit) {
|
|
// XXX: this will not report .ignore file; are we OK?
|
|
let pendingIDs = await lazy.CrashSubmit.pendingIDs(dateLimit);
|
|
if (pendingIDs.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
const uAppDataPath = Services.dirsvc.get("UAppData", Ci.nsIFile).path;
|
|
const pendingDir = PathUtils.join(uAppDataPath, "Crash Reports", "pending");
|
|
|
|
let pendingSHA256 = await Promise.all(
|
|
pendingIDs
|
|
.map(id => {
|
|
return { id, path: PathUtils.join(pendingDir, `${id}.dmp`) };
|
|
})
|
|
.filter(async e => {
|
|
return await IOUtils.exists(e.path);
|
|
})
|
|
.map(async e => {
|
|
return {
|
|
id: e.id,
|
|
sha256: await lazy.CrashServiceUtils.computeMinidumpHash(e.path),
|
|
};
|
|
})
|
|
);
|
|
|
|
return pendingSHA256;
|
|
},
|
|
|
|
async checkForInterestingUnsubmittedCrash(records) {
|
|
let dateLimit = new Date();
|
|
dateLimit.setDate(dateLimit.getDate() - PENDING_REMOTE_CRASH_REPORT_DAYS);
|
|
|
|
const recordHashes = new Set(records.flatMap(entry => entry.hashes));
|
|
let pendingSHA256 = await this.getPendingSha256(dateLimit);
|
|
let matches = pendingSHA256
|
|
.filter(pendingCrash => recordHashes.has(pendingCrash.sha256))
|
|
.map(pendingCrash => pendingCrash.id);
|
|
|
|
return matches;
|
|
},
|
|
|
|
async checkInterestingCrashesAndMaybeNotify(records) {
|
|
const neverShowAgain = Services.prefs.getBoolPref(
|
|
"browser.crashReports.requestedNeverShowAgain"
|
|
);
|
|
if (neverShowAgain) {
|
|
return;
|
|
}
|
|
|
|
let matches = await this.checkForInterestingUnsubmittedCrash(records);
|
|
if (this._showCallback && matches.length) {
|
|
await this._showCallback(matches, true);
|
|
}
|
|
},
|
|
};
|