mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-10-31 16:28:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			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);
 | |
|     }
 | |
|   },
 | |
| };
 | 
