diff options
Diffstat (limited to 'toolkit/components/places/tests/browser/previews/browser_thumbnails.js')
-rw-r--r-- | toolkit/components/places/tests/browser/previews/browser_thumbnails.js | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/browser/previews/browser_thumbnails.js b/toolkit/components/places/tests/browser/previews/browser_thumbnails.js new file mode 100644 index 0000000000..ef776694c2 --- /dev/null +++ b/toolkit/components/places/tests/browser/previews/browser_thumbnails.js @@ -0,0 +1,174 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests PlacesPreviews.jsm + */ +const { PlacesPreviews } = ChromeUtils.importESModule( + "resource://gre/modules/PlacesPreviews.sys.mjs" +); +const { PlacesTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PlacesTestUtils.sys.mjs" +); + +const TEST_URL1 = "http://example.com/"; +const TEST_URL2 = "http://example.org/"; + +/** + * Counts tombstone entries. + * @returns {integer} number of tombstone entries. + */ +async function countTombstones() { + await PlacesTestUtils.promiseAsyncUpdates(); + let db = await PlacesUtils.promiseDBConnection(); + return ( + await db.execute("SELECT count(*) FROM moz_previews_tombstones") + )[0].getResultByIndex(0); +} + +add_task(async function test_thumbnail() { + registerCleanupFunction(async () => { + await PlacesUtils.history.clear(); + // Ensure tombstones table has been emptied. + await TestUtils.waitForCondition(async () => { + return (await countTombstones()) == 0; + }); + PlacesPreviews.testSetDeletionTimeout(null); + }); + // Sanity check initial state. + Assert.equal(await countTombstones(), 0, "There's no tombstone entries"); + + info("Test preview creation and storage."); + await BrowserTestUtils.withNewTab(TEST_URL1, async browser => { + await retryUpdatePreview(browser.currentURI.spec); + let filePath = PlacesPreviews.getPathForUrl(TEST_URL1); + Assert.ok(await IOUtils.exists(filePath), "The screenshot exists"); + Assert.equal( + filePath.substring(filePath.lastIndexOf(".")), + PlacesPreviews.fileExtension, + "Check extension" + ); + await testImageFile(filePath); + await testMozPageThumb(TEST_URL1); + }); +}); + +add_task(async function test_page_removal() { + info("Store another preview and test page removal."); + await BrowserTestUtils.withNewTab(TEST_URL2, async browser => { + await retryUpdatePreview(browser.currentURI.spec); + let filePath = PlacesPreviews.getPathForUrl(TEST_URL2); + Assert.ok(await IOUtils.exists(filePath), "The screenshot exists"); + }); + + // Set deletion time to a small value so it runs immediately. + PlacesPreviews.testSetDeletionTimeout(0); + info("Wait for deletion, check one preview is removed, not the other one."); + let promiseDeleted = new Promise(resolve => { + PlacesPreviews.once("places-preview-deleted", (topic, filePath) => { + resolve(filePath); + }); + }); + await PlacesUtils.history.remove(TEST_URL1); + + let deletedFilePath = await promiseDeleted; + Assert.ok( + !(await IOUtils.exists(deletedFilePath)), + "Check deleted file has been removed" + ); + + info("Check tombstones table has been emptied."); + Assert.equal(await countTombstones(), 0, "There's no tombstone entries"); + + info("Check the other thumbnail has not been removed."); + let path = PlacesPreviews.getPathForUrl(TEST_URL2); + Assert.ok(await IOUtils.exists(path), "Check non-deleted url is still there"); + await testImageFile(path); + await testMozPageThumb(TEST_URL2); +}); + +add_task(async function async_test_deleteOrphans() { + let path = PlacesPreviews.getPathForUrl(TEST_URL2); + Assert.ok(await IOUtils.exists(path), "Sanity check one preview exists"); + // Create a file in the given path that doesn't have an entry in Places. + let fakePath = PathUtils.join( + PlacesPreviews.getPath(), + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." + PlacesPreviews.fileExtension + ); + // File contents don't matter. + await IOUtils.writeJSON(fakePath, { test: true }); + let promiseDeleted = new Promise(resolve => { + PlacesPreviews.once("places-preview-deleted", (topic, filePath) => { + resolve(filePath); + }); + }); + + await PlacesPreviews.deleteOrphans(); + let deletedFilePath = await promiseDeleted; + Assert.equal(deletedFilePath, fakePath, "Check orphan has been deleted"); + Assert.equal(await countTombstones(), 0, "There's no tombstone entries left"); + Assert.ok( + !(await IOUtils.exists(fakePath)), + "Ensure orphan has been deleted" + ); + + Assert.ok(await IOUtils.exists(path), "Ensure valid preview is still there"); +}); + +async function testImageFile(path) { + info("Load the file and check its content type."); + const buffer = await IOUtils.read(path); + const fourcc = new TextDecoder("utf-8").decode(buffer.slice(8, 12)); + Assert.equal(fourcc, "WEBP", "Check the stored preview is webp"); +} + +async function testMozPageThumb(url) { + info("Check moz-page-thumb protocol: " + PlacesPreviews.getPageThumbURL(url)); + let { data, contentType } = await fetchImage( + PlacesPreviews.getPageThumbURL(url) + ); + Assert.equal( + contentType, + PlacesPreviews.fileContentType, + "Check the content type" + ); + const fourcc = data.slice(8, 12); + Assert.equal(fourcc, "WEBP", "Check the returned preview is webp"); +} + +function fetchImage(url) { + return new Promise((resolve, reject) => { + NetUtil.asyncFetch( + { + uri: NetUtil.newURI(url), + loadUsingSystemPrincipal: true, + contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE, + }, + (input, status, request) => { + if (!Components.isSuccessCode(status)) { + reject(new Error("unable to load image")); + return; + } + + try { + let data = NetUtil.readInputStreamToString(input, input.available()); + let contentType = request.QueryInterface(Ci.nsIChannel).contentType; + input.close(); + resolve({ data, contentType }); + } catch (ex) { + reject(ex); + } + } + ); + }); +} + +/** + * Sometimes on macOS fetching the preview fails for timeout/network reasons, + * this retries so the test doesn't intermittently fail over it. + * @param {string} url The url to store a preview for. + * @returns {Promise} resolved once a preview has been captured. + */ +function retryUpdatePreview(url) { + return TestUtils.waitForCondition(() => PlacesPreviews.update(url)); +} |