/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ ChromeUtils.defineESModuleGetters(this, { PageDataService: "resource:///modules/pagedata/PageDataService.sys.mjs", PromiseUtils: "resource://gre/modules/PromiseUtils.sys.mjs", }); // Test that urls are retrieved in the expected order. add_task(async function test_queueOrder() { Services.prefs.setIntPref("browser.pagedata.maxBackgroundFetches", 0); // Pretend we are idle. PageDataService.observe(null, "idle", null); let pageDataResults = [ { date: Date.now(), url: "http://www.mozilla.org/1", siteName: "Mozilla", data: {}, }, { date: Date.now() - 3600, url: "http://www.google.com/2", siteName: "Google", data: {}, }, { date: Date.now() + 3600, url: "http://www.example.com/3", image: "http://www.example.com/banner.jpg", data: {}, }, { date: Date.now() / 2, url: "http://www.wikipedia.org/4", data: {}, }, { date: Date.now() / 3, url: "http://www.microsoft.com/5", data: { [PageDataSchema.DATA_TYPE.PRODUCT]: { name: "Windows 11", }, }, }, ]; let requests = []; PageDataService.fetchPageData = url => { requests.push(url); for (let pageData of pageDataResults) { if (pageData.url == url) { return Promise.resolve(pageData); } } return Promise.reject(new Error("Unknown url")); }; let { promise: completePromise, resolve } = PromiseUtils.defer(); let results = []; let listener = (_, pageData) => { results.push(pageData); if (results.length == pageDataResults.length) { resolve(); } }; PageDataService.on("page-data", listener); for (let pageData of pageDataResults) { PageDataService.queueFetch(pageData.url); } await completePromise; PageDataService.off("page-data", listener); Assert.deepEqual( requests, pageDataResults.map(pd => pd.url) ); // Because our fetch implementation is essentially synchronous the results // will be in a known order. This isn't guaranteed by the API though. Assert.deepEqual(results, pageDataResults); delete PageDataService.fetchPageData; }); // Tests that limiting the number of fetches works. add_task(async function test_queueLimit() { Services.prefs.setIntPref("browser.pagedata.maxBackgroundFetches", 3); // Pretend we are idle. PageDataService.observe(null, "idle", null); let requests = []; PageDataService.fetchPageData = url => { let { promise, resolve, reject } = PromiseUtils.defer(); requests.push({ url, resolve, reject }); return promise; }; let results = []; let listener = (_, pageData) => { results.push(pageData?.url); }; PageDataService.on("page-data", listener); PageDataService.queueFetch("https://www.mozilla.org/1"); PageDataService.queueFetch("https://www.mozilla.org/2"); PageDataService.queueFetch("https://www.mozilla.org/3"); PageDataService.queueFetch("https://www.mozilla.org/4"); PageDataService.queueFetch("https://www.mozilla.org/5"); PageDataService.queueFetch("https://www.mozilla.org/6"); PageDataService.queueFetch("https://www.mozilla.org/7"); PageDataService.queueFetch("https://www.mozilla.org/8"); PageDataService.queueFetch("https://www.mozilla.org/9"); PageDataService.queueFetch("https://www.mozilla.org/10"); PageDataService.queueFetch("https://www.mozilla.org/11"); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", ] ); // Completing or rejecting a request should start new ones. requests[1].resolve({ date: 2345, url: "https://www.mozilla.org/2", siteName: "Test 2", data: {}, }); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", ] ); requests[3].reject(new Error("Fail")); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", "https://www.mozilla.org/5", ] ); // Increasing the limit should start more requests. Services.prefs.setIntPref("browser.pagedata.maxBackgroundFetches", 5); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", "https://www.mozilla.org/5", "https://www.mozilla.org/6", "https://www.mozilla.org/7", ] ); // Dropping the limit shouldn't start anything new. Services.prefs.setIntPref("browser.pagedata.maxBackgroundFetches", 3); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", "https://www.mozilla.org/5", "https://www.mozilla.org/6", "https://www.mozilla.org/7", ] ); // But resolving should also not start new requests. requests[5].resolve({ date: 345334, url: "https://www.mozilla.org/6", siteName: "Test 6", data: {}, }); requests[0].resolve({ date: 343446434, url: "https://www.mozilla.org/1", siteName: "Test 1", data: {}, }); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", "https://www.mozilla.org/5", "https://www.mozilla.org/6", "https://www.mozilla.org/7", ] ); // Until a previous request completes. requests[4].resolve(null); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", "https://www.mozilla.org/5", "https://www.mozilla.org/6", "https://www.mozilla.org/7", "https://www.mozilla.org/8", ] ); // Inifinite queue should work. Services.prefs.setIntPref("browser.pagedata.maxBackgroundFetches", 0); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", "https://www.mozilla.org/5", "https://www.mozilla.org/6", "https://www.mozilla.org/7", "https://www.mozilla.org/8", "https://www.mozilla.org/9", "https://www.mozilla.org/10", "https://www.mozilla.org/11", ] ); requests[10].resolve({ date: 345334, url: "https://www.mozilla.org/11", data: {}, }); requests[2].resolve({ date: 345334, url: "https://www.mozilla.org/3", data: {}, }); requests[7].resolve({ date: 345334, url: "https://www.mozilla.org/8", data: {}, }); requests[6].resolve({ date: 345334, url: "https://www.mozilla.org/7", data: {}, }); requests[8].resolve({ date: 345334, url: "https://www.mozilla.org/9", data: {}, }); requests[9].resolve({ date: 345334, url: "https://www.mozilla.org/10", data: {}, }); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", "https://www.mozilla.org/5", "https://www.mozilla.org/6", "https://www.mozilla.org/7", "https://www.mozilla.org/8", "https://www.mozilla.org/9", "https://www.mozilla.org/10", "https://www.mozilla.org/11", ] ); PageDataService.off("page-data", listener); delete PageDataService.fetchPageData; Assert.deepEqual(results, [ "https://www.mozilla.org/2", "https://www.mozilla.org/6", "https://www.mozilla.org/1", "https://www.mozilla.org/11", "https://www.mozilla.org/3", "https://www.mozilla.org/8", "https://www.mozilla.org/7", "https://www.mozilla.org/9", "https://www.mozilla.org/10", ]); }); // Tests that the user idle state stops and starts fetches. add_task(async function test_idle() { Services.prefs.setIntPref("browser.pagedata.maxBackgroundFetches", 3); // Pretend we are active. PageDataService.observe(null, "active", null); let requests = []; PageDataService.fetchPageData = url => { let { promise, resolve, reject } = PromiseUtils.defer(); requests.push({ url, resolve, reject }); return promise; }; let results = []; let listener = (_, pageData) => { results.push(pageData?.url); }; PageDataService.on("page-data", listener); PageDataService.queueFetch("https://www.mozilla.org/1"); PageDataService.queueFetch("https://www.mozilla.org/2"); PageDataService.queueFetch("https://www.mozilla.org/3"); PageDataService.queueFetch("https://www.mozilla.org/4"); PageDataService.queueFetch("https://www.mozilla.org/5"); PageDataService.queueFetch("https://www.mozilla.org/6"); PageDataService.queueFetch("https://www.mozilla.org/7"); // Let a tick pass. await Promise.resolve(); // Nothing will start when active. Assert.deepEqual( requests.map(r => r.url), [] ); // Pretend we are idle. PageDataService.observe(null, "idle", null); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", ] ); // Completing or rejecting a request should start new ones. requests[1].resolve({ date: 2345, url: "https://www.mozilla.org/2", data: {}, }); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", ] ); // But not when active PageDataService.observe(null, "active", null); requests[3].resolve({ date: 2345, url: "https://www.mozilla.org/4", data: {}, }); requests[0].resolve({ date: 2345, url: "https://www.mozilla.org/1", data: {}, }); requests[2].resolve({ date: 2345, url: "https://www.mozilla.org/3", data: {}, }); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", ] ); // Going idle should start more workers PageDataService.observe(null, "idle", null); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", "https://www.mozilla.org/5", "https://www.mozilla.org/6", "https://www.mozilla.org/7", ] ); requests[4].resolve({ date: 2345, url: "https://www.mozilla.org/5", data: {}, }); requests[5].resolve({ date: 2345, url: "https://www.mozilla.org/6", data: {}, }); requests[6].resolve({ date: 2345, url: "https://www.mozilla.org/7", data: {}, }); // Let a tick pass. await Promise.resolve(); Assert.deepEqual( requests.map(r => r.url), [ "https://www.mozilla.org/1", "https://www.mozilla.org/2", "https://www.mozilla.org/3", "https://www.mozilla.org/4", "https://www.mozilla.org/5", "https://www.mozilla.org/6", "https://www.mozilla.org/7", ] ); PageDataService.off("page-data", listener); delete PageDataService.fetchPageData; Assert.deepEqual(results, [ "https://www.mozilla.org/2", "https://www.mozilla.org/4", "https://www.mozilla.org/1", "https://www.mozilla.org/3", "https://www.mozilla.org/5", "https://www.mozilla.org/6", "https://www.mozilla.org/7", ]); });