test_PlacesBackupResource.js (12158B)
1 /* Any copyright is dedicated to the Public Domain. 2 https://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { PlacesBackupResource } = ChromeUtils.importESModule( 7 "resource:///modules/backup/PlacesBackupResource.sys.mjs" 8 ); 9 const { PlacesDBUtils } = ChromeUtils.importESModule( 10 "resource://gre/modules/PlacesDBUtils.sys.mjs" 11 ); 12 13 registerCleanupFunction(() => { 14 /** 15 * Even though test_backup_no_saved_history clears user prefs too, 16 * clear them here as well in case that test fails and we don't 17 * reach the end of the test, which handles the cleanup. 18 */ 19 Services.prefs.clearUserPref(HISTORY_ENABLED_PREF); 20 Services.prefs.clearUserPref(SANITIZE_ON_SHUTDOWN_PREF); 21 }); 22 23 /** 24 * Tests that we can measure Places DB related files in the profile directory. 25 */ 26 add_task(async function test_measure() { 27 Services.fog.testResetFOG(); 28 29 const EXPECTED_PLACES_DB_SIZE = 5240; 30 const EXPECTED_FAVICONS_DB_SIZE = 5240; 31 32 // Create resource files in temporary directory 33 const tempDir = PathUtils.tempDir; 34 let tempPlacesDBPath = PathUtils.join(tempDir, "places.sqlite"); 35 let tempFaviconsDBPath = PathUtils.join(tempDir, "favicons.sqlite"); 36 await createKilobyteSizedFile(tempPlacesDBPath, EXPECTED_PLACES_DB_SIZE); 37 await createKilobyteSizedFile(tempFaviconsDBPath, EXPECTED_FAVICONS_DB_SIZE); 38 39 let placesBackupResource = new PlacesBackupResource(); 40 await placesBackupResource.measure(tempDir); 41 42 let placesMeasurement = Glean.browserBackup.placesSize.testGetValue(); 43 let faviconsMeasurement = Glean.browserBackup.faviconsSize.testGetValue(); 44 let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false); 45 46 // Compare glean vs telemetry measurements 47 TelemetryTestUtils.assertScalar( 48 scalars, 49 "browser.backup.places_size", 50 placesMeasurement, 51 "Glean and telemetry measurements for places.sqlite should be equal" 52 ); 53 TelemetryTestUtils.assertScalar( 54 scalars, 55 "browser.backup.favicons_size", 56 faviconsMeasurement, 57 "Glean and telemetry measurements for favicons.sqlite should be equal" 58 ); 59 60 // Compare glean measurements vs actual file sizes 61 Assert.equal( 62 placesMeasurement, 63 EXPECTED_PLACES_DB_SIZE, 64 "Should have collected the correct glean measurement for places.sqlite" 65 ); 66 Assert.equal( 67 faviconsMeasurement, 68 EXPECTED_FAVICONS_DB_SIZE, 69 "Should have collected the correct glean measurement for favicons.sqlite" 70 ); 71 72 await maybeRemovePath(tempPlacesDBPath); 73 await maybeRemovePath(tempFaviconsDBPath); 74 }); 75 76 /** 77 * Tests that the backup method correctly copies places.sqlite and 78 * favicons.sqlite from the profile directory into the staging directory. 79 */ 80 add_task(async function test_backup() { 81 Services.fog.testResetFOG(); 82 const placesTimeHistogram = TelemetryTestUtils.getAndClearHistogram( 83 "BROWSER_BACKUP_PLACES_TIME_MS" 84 ); 85 const faviconsTimeHistogram = TelemetryTestUtils.getAndClearHistogram( 86 "BROWSER_BACKUP_FAVICONS_TIME_MS" 87 ); 88 let sandbox = sinon.createSandbox(); 89 90 let placesBackupResource = new PlacesBackupResource(); 91 let sourcePath = await IOUtils.createUniqueDirectory( 92 PathUtils.tempDir, 93 "PlacesBackupResource-source-test" 94 ); 95 let stagingPath = await IOUtils.createUniqueDirectory( 96 PathUtils.tempDir, 97 "PlacesBackupResource-staging-test" 98 ); 99 100 // Make sure these files exist in the source directory, otherwise 101 // BackupResource will skip attempting to back them up. 102 await createTestFiles(sourcePath, [ 103 { path: "places.sqlite" }, 104 { path: "favicons.sqlite" }, 105 ]); 106 107 let fakeConnection = { 108 backup: sandbox.stub().resolves(true), 109 close: sandbox.stub().resolves(true), 110 }; 111 sandbox.stub(Sqlite, "openConnection").returns(fakeConnection); 112 sandbox.stub(PlacesDBUtils, "removeDownloadsMetadataFromDb"); 113 114 let manifestEntry = await placesBackupResource.backup( 115 stagingPath, 116 sourcePath 117 ); 118 Assert.equal( 119 manifestEntry, 120 null, 121 "PlacesBackupResource.backup should return null as its ManifestEntry" 122 ); 123 124 Assert.ok( 125 PlacesDBUtils.removeDownloadsMetadataFromDb.calledOnce, 126 "PlacesDBUtils.removeDownloadsMetadataFromDb was called" 127 ); 128 Assert.ok( 129 fakeConnection.backup.calledTwice, 130 "Backup should have been called twice" 131 ); 132 Assert.ok( 133 fakeConnection.backup.firstCall.calledWith( 134 PathUtils.join(stagingPath, "places.sqlite") 135 ), 136 "places.sqlite should have been backed up first" 137 ); 138 Assert.ok( 139 fakeConnection.backup.secondCall.calledWith( 140 PathUtils.join(stagingPath, "favicons.sqlite") 141 ), 142 "favicons.sqlite should have been backed up second" 143 ); 144 // Validate timing metrics 145 assertSingleTimeMeasurement(Glean.browserBackup.placesTime.testGetValue()); 146 assertSingleTimeMeasurement(Glean.browserBackup.faviconsTime.testGetValue()); 147 assertHistogramMeasurementQuantity(placesTimeHistogram, 1); 148 assertHistogramMeasurementQuantity(faviconsTimeHistogram, 1); 149 150 await maybeRemovePath(stagingPath); 151 await maybeRemovePath(sourcePath); 152 153 sandbox.restore(); 154 }); 155 156 /** 157 * Tests that we don't backup history is the user is clearing browsing history 158 * on shutdown. 159 */ 160 add_task(async function test_backup_no_saved_history() { 161 Services.fog.testResetFOG(); 162 const placesTimeHistogram = TelemetryTestUtils.getAndClearHistogram( 163 "BROWSER_BACKUP_PLACES_TIME_MS" 164 ); 165 const faviconsTimeHistogram = TelemetryTestUtils.getAndClearHistogram( 166 "BROWSER_BACKUP_FAVICONS_TIME_MS" 167 ); 168 let sandbox = sinon.createSandbox(); 169 170 let sourcePath = await IOUtils.createUniqueDirectory( 171 PathUtils.tempDir, 172 "PlacesBackupResource-source-test" 173 ); 174 let stagingPath = await IOUtils.createUniqueDirectory( 175 PathUtils.tempDir, 176 "PlacesBackupResource-staging-test" 177 ); 178 179 let fakeConnection = { 180 backup: sandbox.stub().resolves(true), 181 close: sandbox.stub().resolves(true), 182 }; 183 sandbox.stub(Sqlite, "openConnection").returns(fakeConnection); 184 185 /** 186 * First verify that remember history pref alone affects backup file type for places, 187 * despite sanitize on shutdown pref value. 188 */ 189 Services.prefs.setBoolPref(HISTORY_ENABLED_PREF, false); 190 Services.prefs.setBoolPref(SANITIZE_ON_SHUTDOWN_PREF, false); 191 192 Assert.ok( 193 !PlacesBackupResource.canBackupResource, 194 "Cannot backup places when history is disabled" 195 ); 196 197 // PlacesBackupResource should not be called when canBackupResource is false 198 // The test is just verifying the check works correctly 199 // Validate no timing metrics 200 Assert.equal( 201 Glean.browserBackup.placesTime.testGetValue(), 202 null, 203 "Should not have timed places backup when it did not occur" 204 ); 205 Assert.equal( 206 Glean.browserBackup.faviconsTime.testGetValue(), 207 null, 208 "Should not have timed favicons backup when it did not occur" 209 ); 210 assertHistogramMeasurementQuantity(placesTimeHistogram, 0); 211 assertHistogramMeasurementQuantity(faviconsTimeHistogram, 0); 212 213 /** 214 * Now verify that the sanitize shutdown pref also prevents backup of places. 215 */ 216 Services.prefs.setBoolPref(HISTORY_ENABLED_PREF, true); 217 Services.prefs.setBoolPref(SANITIZE_ON_SHUTDOWN_PREF, true); 218 Services.prefs.setBoolPref(HISTORY_CLEARED_ON_SHUTDOWN_PREF, true); 219 220 Assert.ok( 221 !PlacesBackupResource.canBackupResource, 222 "Cannot backup places when sanitizeOnShutdown and history cleared on shutdown are enabled" 223 ); 224 225 // PlacesBackupResource should not be called when canBackupResource is false 226 // The test is just verifying the check works correctly 227 // Validate no timing metrics 228 Assert.equal( 229 Glean.browserBackup.placesTime.testGetValue(), 230 null, 231 "Should not have timed places backup when it did not occur" 232 ); 233 Assert.equal( 234 Glean.browserBackup.faviconsTime.testGetValue(), 235 null, 236 "Should not have timed favicons backup when it did not occur" 237 ); 238 assertHistogramMeasurementQuantity(placesTimeHistogram, 0); 239 assertHistogramMeasurementQuantity(faviconsTimeHistogram, 0); 240 241 await maybeRemovePath(stagingPath); 242 await maybeRemovePath(sourcePath); 243 244 sandbox.restore(); 245 Services.prefs.clearUserPref(HISTORY_ENABLED_PREF); 246 Services.prefs.clearUserPref(SANITIZE_ON_SHUTDOWN_PREF); 247 Services.prefs.clearUserPref(HISTORY_CLEARED_ON_SHUTDOWN_PREF); 248 }); 249 250 /** 251 * Tests that we don't backup history if permanent private browsing is enabled 252 */ 253 add_task(async function test_backup_private_browsing() { 254 Services.fog.testResetFOG(); 255 const placesTimeHistogram = TelemetryTestUtils.getAndClearHistogram( 256 "BROWSER_BACKUP_PLACES_TIME_MS" 257 ); 258 const faviconsTimeHistogram = TelemetryTestUtils.getAndClearHistogram( 259 "BROWSER_BACKUP_FAVICONS_TIME_MS" 260 ); 261 let sandbox = sinon.createSandbox(); 262 263 let sourcePath = await IOUtils.createUniqueDirectory( 264 PathUtils.tempDir, 265 "PlacesBackupResource-source-test" 266 ); 267 let stagingPath = await IOUtils.createUniqueDirectory( 268 PathUtils.tempDir, 269 "PlacesBackupResource-staging-test" 270 ); 271 272 let fakeConnection = { 273 backup: sandbox.stub().resolves(true), 274 close: sandbox.stub().resolves(true), 275 }; 276 sandbox.stub(Sqlite, "openConnection").returns(fakeConnection); 277 sandbox.stub(PrivateBrowsingUtils, "permanentPrivateBrowsing").value(true); 278 279 Assert.ok( 280 !PlacesBackupResource.canBackupResource, 281 "Cannot backup places when permanent private browsing is enabled" 282 ); 283 284 // PlacesBackupResource should not be called when canBackupResource is false 285 // The test is just verifying the check works correctly 286 // Validate no timing metrics 287 Assert.equal( 288 Glean.browserBackup.placesTime.testGetValue(), 289 null, 290 "Should not have timed places backup when it did not occur" 291 ); 292 Assert.equal( 293 Glean.browserBackup.faviconsTime.testGetValue(), 294 null, 295 "Should not have timed favicons backup when it did not occur" 296 ); 297 assertHistogramMeasurementQuantity(placesTimeHistogram, 0); 298 assertHistogramMeasurementQuantity(faviconsTimeHistogram, 0); 299 300 await maybeRemovePath(stagingPath); 301 await maybeRemovePath(sourcePath); 302 303 sandbox.restore(); 304 }); 305 306 /** 307 * Test that the recover method correctly copies places.sqlite and favicons.sqlite 308 * from the recovery directory into the destination profile directory. 309 */ 310 add_task(async function test_recover() { 311 let placesBackupResource = new PlacesBackupResource(); 312 let recoveryPath = await IOUtils.createUniqueDirectory( 313 PathUtils.tempDir, 314 "PlacesBackupResource-recovery-test" 315 ); 316 let destProfilePath = await IOUtils.createUniqueDirectory( 317 PathUtils.tempDir, 318 "PlacesBackupResource-test-profile" 319 ); 320 321 const simpleCopyFiles = [ 322 { path: "places.sqlite" }, 323 { path: "favicons.sqlite" }, 324 ]; 325 await createTestFiles(recoveryPath, simpleCopyFiles); 326 327 // The backup method is expected to have returned a null ManifestEntry 328 let postRecoveryEntry = await placesBackupResource.recover( 329 null /* manifestEntry */, 330 recoveryPath, 331 destProfilePath 332 ); 333 Assert.equal( 334 postRecoveryEntry, 335 null, 336 "PlacesBackupResource.recover should return null as its post recovery entry" 337 ); 338 339 await assertFilesExist(destProfilePath, simpleCopyFiles); 340 341 await maybeRemovePath(recoveryPath); 342 await maybeRemovePath(destProfilePath); 343 }); 344 345 /** 346 * Tests the canBackupResource method with various pref configurations. 347 */ 348 add_task(async function test_canBackupResource() { 349 Assert.ok( 350 PlacesBackupResource.canBackupResource, 351 "Should be able to backup by default" 352 ); 353 354 Services.prefs.setBoolPref(HISTORY_ENABLED_PREF, false); 355 Assert.ok( 356 !PlacesBackupResource.canBackupResource, 357 "Cannot backup when history is disabled" 358 ); 359 Services.prefs.clearUserPref(HISTORY_ENABLED_PREF); 360 361 Assert.ok( 362 PlacesBackupResource.canBackupResource, 363 "Should be able to backup after clearing pref" 364 ); 365 366 Services.prefs.setBoolPref(SANITIZE_ON_SHUTDOWN_PREF, true); 367 Services.prefs.setBoolPref(HISTORY_CLEARED_ON_SHUTDOWN_PREF, true); 368 Assert.ok( 369 !PlacesBackupResource.canBackupResource, 370 "Cannot backup when sanitizeOnShutdown and history cleared on shutdown are enabled" 371 ); 372 373 Services.prefs.clearUserPref(SANITIZE_ON_SHUTDOWN_PREF); 374 Services.prefs.clearUserPref(HISTORY_CLEARED_ON_SHUTDOWN_PREF); 375 });