"use strict"; const { AddonTestUtils } = ChromeUtils.import( "resource://testing-common/AddonTestUtils.jsm" ); ChromeUtils.defineESModuleGetters(this, { SearchTestUtils: "resource://testing-common/SearchTestUtils.sys.mjs", UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs", UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.sys.mjs", UrlbarQueryContext: "resource:///modules/UrlbarUtils.sys.mjs", UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs", UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs", }); XPCOMUtils.defineLazyModuleGetters(this, { ExtensionParent: "resource://gre/modules/ExtensionParent.jsm", }); AddonTestUtils.init(this); AddonTestUtils.overrideCertDB(); AddonTestUtils.createAppInfo( "xpcshell@tests.mozilla.org", "XPCShell", "1", "42" ); SearchTestUtils.init(this); SearchTestUtils.initXPCShellAddonManager(this, "system"); function promiseUninstallCompleted(extensionId) { return new Promise(resolve => { // eslint-disable-next-line mozilla/balanced-listeners ExtensionParent.apiManager.on("uninstall-complete", (type, { id }) => { if (id === extensionId) { executeSoon(resolve); } }); }); } function getPayload(result) { let payload = {}; for (let [key, value] of Object.entries(result.payload)) { if (value !== undefined) { payload[key] = value; } } return payload; } add_task(async function startup() { Services.prefs.setCharPref("browser.search.region", "US"); Services.prefs.setIntPref("browser.search.addonLoadTimeout", 0); Services.prefs.setBoolPref( "browser.search.separatePrivateDefault.ui.enabled", false ); // Set the notification timeout to a really high value to avoid intermittent // failures due to the mock extensions not responding in time. Services.prefs.setIntPref("browser.urlbar.extension.timeout", 5000); registerCleanupFunction(() => { Services.prefs.clearUserPref("browser.urlbar.extension.timeout"); }); await AddonTestUtils.promiseStartupManager(); await UrlbarTestUtils.initXPCShellDependencies(); // Add a test engine and make it default so that when we do searches below, // Firefox doesn't try to include search suggestions from the actual default // engine from over the network. await SearchTestUtils.installSearchExtension( { name: "Test engine", keyword: "@testengine", search_url_get_params: "s={searchTerms}", }, { setAsDefault: true } ); }); // Extensions must specify the "urlbar" permission to use browser.urlbar. add_task(async function test_urlbar_without_urlbar_permission() { let ext = ExtensionTestUtils.loadExtension({ isPrivileged: true, background() { browser.test.assertEq( browser.urlbar, undefined, "'urlbar' permission is required" ); }, }); await ext.startup(); await ext.unload(); }); // Extensions must be privileged to use browser.urlbar. add_task(async function test_urlbar_no_privilege() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, background() { browser.test.assertEq( browser.urlbar, undefined, "'urlbar' permission is privileged" ); }, }); await ext.startup(); await ext.unload(); }); // Extensions must be privileged to use browser.urlbar. add_task(async function test_urlbar_temporary_without_privilege() { let extension = ExtensionTestUtils.loadExtension({ temporarilyInstalled: true, isPrivileged: false, manifest: { permissions: ["urlbar"], }, }); ExtensionTestUtils.failOnSchemaWarnings(false); let { messages } = await promiseConsoleOutput(async () => { await Assert.rejects( extension.startup(), /Using the privileged permission/, "Startup failed with privileged permission" ); }); ExtensionTestUtils.failOnSchemaWarnings(true); AddonTestUtils.checkMessages( messages, { expected: [ { message: /Using the privileged permission 'urlbar' requires a privileged add-on/, }, ], }, true ); }); // Checks that providers are added and removed properly. add_task(async function test_registerProvider() { // A copy of the default providers. let providers = UrlbarProvidersManager.providers.slice(); let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { for (let state of ["active", "inactive", "restricting"]) { let name = `Test-${state}`; browser.urlbar.onBehaviorRequested.addListener(query => { browser.test.assertFalse(query.isPrivate, "Context is non private"); browser.test.assertEq(query.maxResults, 10, "Check maxResults"); browser.test.assertTrue( query.searchString, "SearchString is non empty" ); browser.test.assertTrue( Array.isArray(query.sources), "sources is an array" ); return state; }, name); browser.urlbar.onResultsRequested.addListener(query => [], name); } }, }); await ext.startup(); Assert.greater( UrlbarProvidersManager.providers.length, providers.length, "Providers have been added" ); // Run a query, this should execute the above listeners and checks, plus it // will set the provider's isActive and priority. let queryContext = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "*", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(queryContext); // Check the providers behavior has been setup properly. for (let provider of UrlbarProvidersManager.providers) { if (!provider.name.startsWith("Test")) { continue; } let [, state] = provider.name.split("-"); let isActive = state != "inactive"; let restricting = state == "restricting"; Assert.equal( isActive, provider.isActive(queryContext), "Check active callback" ); if (restricting) { Assert.notEqual( provider.getPriority(queryContext), 0, "Check provider priority" ); } else { Assert.equal( provider.getPriority(queryContext), 0, "Check provider priority" ); } } await ext.unload(); // Sanity check the providers. Assert.deepEqual( UrlbarProvidersManager.providers, providers, "Should return to the default providers" ); }); // Adds a single active provider that returns many kinds of results. This also // checks that the heuristic result from the built-in HeuristicFallback provider // is included. add_task(async function test_onProviderResultsRequested() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(query => { return "active"; }, "test"); browser.urlbar.onResultsRequested.addListener(query => { browser.test.assertFalse(query.isPrivate); browser.test.assertEq(query.maxResults, 10); browser.test.assertEq(query.searchString, "test"); browser.test.assertTrue(Array.isArray(query.sources)); return [ { type: "remote_tab", source: "tabs", payload: { title: "Test remote_tab-tabs result", url: "https://example.com/remote_tab-tabs", device: "device", lastUsed: 1621366890, }, }, { type: "search", source: "search", payload: { suggestion: "Test search-search result", engine: "Test engine", }, }, { type: "tab", source: "tabs", payload: { title: "Test tab-tabs result", url: "https://example.com/tab-tabs", }, }, { type: "tip", source: "local", payload: { text: "Test tip-local result text", buttonText: "Test tip-local result button text", buttonUrl: "https://example.com/tip-button", helpUrl: "https://example.com/tip-help", }, }, { type: "url", source: "history", payload: { title: "Test url-history result", url: "https://example.com/url-history", }, }, ]; }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check isActive and priority. Assert.ok(provider.isActive(context)); Assert.equal(provider.getPriority(context), 0); // Check the results. let expectedResults = [ // The first result should be a search result returned by HeuristicFallback. { type: UrlbarUtils.RESULT_TYPE.SEARCH, source: UrlbarUtils.RESULT_SOURCE.SEARCH, title: "test", heuristic: true, payload: { query: "test", engine: "Test engine", }, }, // The second result should be our search suggestion result since the // default muxer sorts search suggestion results before other types. { type: UrlbarUtils.RESULT_TYPE.SEARCH, source: UrlbarUtils.RESULT_SOURCE.SEARCH, title: "Test search-search result", heuristic: false, payload: { engine: "Test engine", suggestion: "Test search-search result", }, }, // The rest of the results should appear in the order we returned them // above. { type: UrlbarUtils.RESULT_TYPE.REMOTE_TAB, source: UrlbarUtils.RESULT_SOURCE.TABS, title: "Test remote_tab-tabs result", heuristic: false, payload: { title: "Test remote_tab-tabs result", url: "https://example.com/remote_tab-tabs", displayUrl: "example.com/remote_tab-tabs", device: "device", lastUsed: 1621366890, }, }, { type: UrlbarUtils.RESULT_TYPE.TAB_SWITCH, source: UrlbarUtils.RESULT_SOURCE.TABS, title: "Test tab-tabs result", heuristic: false, payload: { title: "Test tab-tabs result", url: "https://example.com/tab-tabs", displayUrl: "example.com/tab-tabs", }, }, { type: UrlbarUtils.RESULT_TYPE.TIP, source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, title: "", heuristic: false, payload: { text: "Test tip-local result text", buttonText: "Test tip-local result button text", buttonUrl: "https://example.com/tip-button", helpUrl: "https://example.com/tip-help", type: "extension", }, }, { type: UrlbarUtils.RESULT_TYPE.URL, source: UrlbarUtils.RESULT_SOURCE.HISTORY, title: "Test url-history result", heuristic: false, payload: { title: "Test url-history result", url: "https://example.com/url-history", displayUrl: "example.com/url-history", }, }, ]; Assert.ok(context.results.every(r => !r.hasSuggestedIndex)); let actualResults = context.results.map(r => ({ type: r.type, source: r.source, title: r.title, heuristic: r.heuristic, payload: getPayload(r), })); Assert.deepEqual(actualResults, expectedResults); await ext.unload(); }); // Extensions can specify search engines using engine names, aliases, and URLs. add_task(async function test_onProviderResultsRequested_searchEngines() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(query => { return "restricting"; }, "test"); browser.urlbar.onResultsRequested.addListener(query => { return [ { type: "search", source: "search", payload: { engine: "Test engine", suggestion: "engine specified", }, }, { type: "search", source: "search", payload: { keyword: "@testengine", suggestion: "keyword specified", }, }, { type: "search", source: "search", payload: { url: "https://example.com/?s", suggestion: "url specified", }, }, { type: "search", source: "search", payload: { engine: "Test engine", keyword: "@testengine", url: "https://example.com/?s", suggestion: "engine, keyword, and url specified", }, }, { type: "search", source: "search", payload: { keyword: "@testengine", url: "https://example.com/?s", suggestion: "keyword and url specified", }, }, { type: "search", source: "search", payload: { suggestion: "no engine", }, }, { type: "search", source: "search", payload: { engine: "bogus", suggestion: "no matching engine", }, }, { type: "search", source: "search", payload: { keyword: "@bogus", suggestion: "no matching keyword", }, }, { type: "search", source: "search", payload: { url: "http://bogus-no-search-engine.com/", suggestion: "no matching url", }, }, { type: "search", source: "search", payload: { url: "bogus", suggestion: "invalid url", }, }, { type: "search", source: "search", payload: { url: "foo:bar", suggestion: "url with no hostname", }, }, ]; }, "test"); }, }); await ext.startup(); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check the results. The first several are valid and should include "Test // engine" as the engine. The others don't specify an engine and are // therefore invalid, so they should be ignored. let expectedResults = [ { type: UrlbarUtils.RESULT_TYPE.SEARCH, source: UrlbarUtils.RESULT_SOURCE.SEARCH, engine: "Test engine", title: "engine specified", heuristic: false, }, { type: UrlbarUtils.RESULT_TYPE.SEARCH, source: UrlbarUtils.RESULT_SOURCE.SEARCH, engine: "Test engine", title: "keyword specified", heuristic: false, }, { type: UrlbarUtils.RESULT_TYPE.SEARCH, source: UrlbarUtils.RESULT_SOURCE.SEARCH, engine: "Test engine", title: "url specified", heuristic: false, }, { type: UrlbarUtils.RESULT_TYPE.SEARCH, source: UrlbarUtils.RESULT_SOURCE.SEARCH, engine: "Test engine", title: "engine, keyword, and url specified", heuristic: false, }, { type: UrlbarUtils.RESULT_TYPE.SEARCH, source: UrlbarUtils.RESULT_SOURCE.SEARCH, engine: "Test engine", title: "keyword and url specified", heuristic: false, }, ]; let actualResults = context.results.map(r => ({ type: r.type, source: r.source, engine: r.payload.engine || null, title: r.title, heuristic: r.heuristic, })); Assert.deepEqual(actualResults, expectedResults); await ext.unload(); }); // Adds two providers, one active and one inactive. Only the active provider // should be asked to return results. add_task(async function test_activeAndInactiveProviders() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { for (let behavior of ["active", "inactive"]) { browser.urlbar.onBehaviorRequested.addListener(query => { return behavior; }, behavior); browser.urlbar.onResultsRequested.addListener(query => { browser.test.assertEq( behavior, "active", "onResultsRequested should be fired only for the active provider" ); return [ { type: "url", source: "history", payload: { title: `Test result ${behavior}`, url: `https://example.com/${behavior}`, }, }, ]; }, behavior); } }, }); await ext.startup(); // Check the providers. let active = UrlbarProvidersManager.getProvider("active"); let inactive = UrlbarProvidersManager.getProvider("inactive"); Assert.ok(active); Assert.ok(inactive); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check isActive and priority. Assert.ok(active.isActive(context)); Assert.ok(!inactive.isActive(context)); Assert.equal(active.getPriority(context), 0); Assert.equal(inactive.getPriority(context), 0); // Check the results. Assert.equal(context.results.length, 2); Assert.ok(context.results[0].heuristic); Assert.equal(context.results[1].title, "Test result active"); await ext.unload(); }); // Adds three active providers. They all should be asked for results. add_task(async function test_threeActiveProviders() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { for (let i = 0; i < 3; i++) { let name = `test-${i}`; browser.urlbar.onBehaviorRequested.addListener(query => { return "active"; }, name); browser.urlbar.onResultsRequested.addListener(query => { return [ { type: "url", source: "history", payload: { title: `Test result ${i}`, url: `https://example.com/${i}`, }, }, ]; }, name); } }, }); await ext.startup(); // Check the providers. let providers = []; for (let i = 0; i < 3; i++) { let name = `test-${i}`; let provider = UrlbarProvidersManager.getProvider(name); Assert.ok(provider); providers.push(provider); } // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check isActive and priority. for (let provider of providers) { Assert.ok(provider.isActive(context)); Assert.equal(provider.getPriority(context), 0); } // Check the results. Assert.equal(context.results.length, 4); Assert.ok(context.results[0].heuristic); for (let i = 0; i < providers.length; i++) { Assert.equal(context.results[i + 1].title, `Test result ${i}`); } await ext.unload(); }); // Adds three inactive providers. None of them should be asked for results. // This also checks that provider behavior is "inactive" by default. add_task(async function test_threeInactiveProviders() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { for (let i = 0; i < 3; i++) { // Don't add an onBehaviorRequested listener. That way we can test that // the default behavior is inactive. browser.urlbar.onResultsRequested.addListener(query => { browser.test.notifyFail( "onResultsRequested fired for inactive provider" ); }, `test-${i}`); } }, }); await ext.startup(); // Check the providers. let providers = []; for (let i = 0; i < 3; i++) { let name = `test-${i}`; let provider = UrlbarProvidersManager.getProvider(name); Assert.ok(provider); providers.push(provider); } // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check isActive and priority. for (let provider of providers) { Assert.ok(!provider.isActive(context)); Assert.equal(provider.getPriority(context), 0); } // Check the results. Assert.equal(context.results.length, 1); Assert.ok(context.results[0].heuristic); await ext.unload(); }); // Adds active, inactive, and restricting providers. Only the restricting // provider should be asked to return results. add_task(async function test_activeInactiveAndRestrictingProviders() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { for (let behavior of ["active", "inactive", "restricting"]) { browser.urlbar.onBehaviorRequested.addListener(query => { return behavior; }, behavior); browser.urlbar.onResultsRequested.addListener(query => { browser.test.assertEq( behavior, "restricting", "onResultsRequested should be fired for the restricting provider" ); return [ { type: "url", source: "history", payload: { title: `Test result ${behavior}`, url: `https://example.com/${behavior}`, }, }, ]; }, behavior); } }, }); await ext.startup(); // Check the providers. let providers = {}; for (let behavior of ["active", "inactive", "restricting"]) { let provider = UrlbarProvidersManager.getProvider(behavior); Assert.ok(provider); providers[behavior] = provider; } // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check isActive and isRestricting. Assert.ok(providers.active.isActive(context)); Assert.equal(providers.active.getPriority(context), 0); Assert.ok(!providers.inactive.isActive(context)); Assert.equal(providers.inactive.getPriority(context), 0); Assert.ok(providers.restricting.isActive(context)); Assert.notEqual(providers.restricting.getPriority(context), 0); // Check the results. Assert.equal(context.results.length, 1); Assert.equal(context.results[0].title, "Test result restricting"); await ext.unload(); }); // Adds a restricting provider that returns a heuristic result. The actual // result created from the extension's result should be a heuristic. add_task(async function test_heuristicRestricting() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(query => { return "restricting"; }, "test"); browser.urlbar.onResultsRequested.addListener(query => { return [ { type: "url", source: "history", heuristic: true, payload: { title: "Test result", url: "https://example.com/", }, }, ]; }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check the results. Assert.equal(context.results.length, 1); Assert.ok(context.results[0].heuristic); await ext.unload(); }); // Adds a non-restricting provider that returns a heuristic result. The actual // result created from the extension's result should *not* be a heuristic, and // the usual UrlbarProviderHeuristicFallback heuristic should be present. add_task(async function test_heuristicNonRestricting() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(query => { return "active"; }, "test"); browser.urlbar.onResultsRequested.addListener(query => { return [ { type: "url", source: "history", heuristic: true, payload: { title: "Test result", url: "https://example.com/", }, }, ]; }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check the results. The first result should be // UrlbarProviderHeuristicFallback's heuristic. let firstResult = context.results[0]; Assert.ok(firstResult.heuristic); Assert.equal(firstResult.type, UrlbarUtils.RESULT_TYPE.SEARCH); Assert.equal(firstResult.source, UrlbarUtils.RESULT_SOURCE.SEARCH); Assert.equal(firstResult.payload.engine, "Test engine"); // The extension result should be present but not the heuristic. let result = context.results.find(r => r.title == "Test result"); Assert.ok(result); Assert.ok(!result.heuristic); await ext.unload(); }); // Adds an active provider that doesn't have a listener for onResultsRequested. // No results should be added. add_task(async function test_onResultsRequestedNotImplemented() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(query => { return "active"; }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check isActive and isRestricting. Assert.ok(provider.isActive(context)); Assert.equal(provider.getPriority(context), 0); // Check the results. Assert.equal(context.results.length, 1); Assert.ok(context.results[0].heuristic); await ext.unload(); }); // Adds an active provider that returns a result with a malformed payload. The // bad result shouldn't be added. add_task(async function test_badPayload() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(query => { return "active"; }, "test"); browser.urlbar.onResultsRequested.addListener(async query => { return [ { type: "url", source: "history", payload: "this is a bad payload", }, { type: "url", source: "history", payload: { title: "Test result", url: "https://example.com/", }, }, ]; }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check the results. Assert.equal(context.results.length, 2); Assert.ok(context.results[0].heuristic); Assert.equal(context.results[1].title, "Test result"); await ext.unload(); }); // Tests the onQueryCanceled event. add_task(async function test_onQueryCanceled() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(query => { return "active"; }, "test"); browser.urlbar.onQueryCanceled.addListener(query => { browser.test.notifyPass("canceled"); }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query but immediately cancel it. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); let startPromise = controller.startQuery(context); controller.cancelQuery(); await startPromise; await ext.awaitFinish("canceled"); await ext.unload(); }); // Adds an onBehaviorRequested listener that takes too long to respond. The // provider should default to inactive. add_task(async function test_onBehaviorRequestedTimeout() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(async query => { // setTimeout is available in background scripts // eslint-disable-next-line mozilla/no-arbitrary-setTimeout, no-undef await new Promise(r => setTimeout(r, 500)); return "active"; }, "test"); browser.urlbar.onResultsRequested.addListener(query => { browser.test.notifyFail( "onResultsRequested fired for inactive provider" ); }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); Services.prefs.setIntPref("browser.urlbar.extension.timeout", 0); await controller.startQuery(context); Services.prefs.clearUserPref("browser.urlbar.extension.timeout"); // Check isActive and priority. Assert.ok(!provider.isActive(context)); Assert.equal(provider.getPriority(context), 0); // Check the results. Assert.equal(context.results.length, 1); Assert.ok(context.results[0].heuristic); await ext.unload(); }); // Adds an onResultsRequested listener that takes too long to respond. The // provider's results should default to no results. add_task(async function test_onResultsRequestedTimeout() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(query => { return "active"; }, "test"); browser.urlbar.onResultsRequested.addListener(async query => { // setTimeout is available in background scripts // eslint-disable-next-line mozilla/no-arbitrary-setTimeout, no-undef await new Promise(r => setTimeout(r, 600)); return [ { type: "url", source: "history", payload: { title: "Test result", url: "https://example.com/", }, }, ]; }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check isActive and priority. Assert.ok(provider.isActive(context)); Assert.equal(provider.getPriority(context), 0); // Check the results. Assert.equal(context.results.length, 1); Assert.ok(context.results[0].heuristic); await ext.unload(); }); // Performs a search in a private context for an extension that does not allow // private browsing. The extension's listeners should not be called. add_task(async function test_privateBrowsing_not_allowed() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], incognito: "not_allowed", }, isPrivileged: true, background() { browser.urlbar.onBehaviorRequested.addListener(query => { browser.test.notifyFail( "onBehaviorRequested fired in private browsing" ); }, "Test-private"); browser.urlbar.onResultsRequested.addListener(query => { browser.test.notifyFail("onResultsRequested fired in private browsing"); }, "Test-private"); // We can't easily test onQueryCanceled here because immediately canceling // the query will cause onResultsRequested not to be fired. // onResultsRequested should in fact not be fired, but that should be // because this test runs in private-browsing mode, not because the query // was canceled. See the next test task for onQueryCanceled. }, }); await ext.startup(); // Run a query, this should execute the above listeners and checks, plus it // will set the provider's isActive and priority. let queryContext = new UrlbarQueryContext({ allowAutofill: false, isPrivate: true, maxResults: 10, searchString: "*", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(queryContext); // Check the providers behavior has been setup properly. let provider = UrlbarProvidersManager.getProvider("Test-private"); Assert.ok(!provider.isActive({}), "Check provider is inactive"); await ext.unload(); }); // Same as the previous task but tests the onQueryCanceled event: Performs a // search in a private context for an extension that does not allow private // browsing. The extension's onQueryCanceled listener should not be called. add_task(async function test_privateBrowsing_not_allowed_onQueryCanceled() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], incognito: "not_allowed", }, isPrivileged: true, background() { browser.urlbar.onBehaviorRequested.addListener(query => { browser.test.notifyFail( "onBehaviorRequested fired in private browsing" ); }, "test"); browser.urlbar.onQueryCanceled.addListener(query => { browser.test.notifyFail("onQueryCanceled fired in private browsing"); }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query but immediately cancel it. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: true, maxResults: 10, searchString: "*", }); let controller = UrlbarTestUtils.newMockController(); let startPromise = controller.startQuery(context); controller.cancelQuery(); await startPromise; // Check isActive and priority. Assert.ok(!provider.isActive(context)); Assert.equal(provider.getPriority(context), 0); await ext.unload(); }); // Performs a search in a private context for an extension that allows private // browsing. The extension's listeners should be called. add_task(async function test_privateBrowsing_allowed() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { let name = "Test-private"; browser.urlbar.onBehaviorRequested.addListener(query => { browser.test.sendMessage("onBehaviorRequested"); return "active"; }, name); browser.urlbar.onResultsRequested.addListener(query => { browser.test.sendMessage("onResultsRequested"); return []; }, name); // We can't easily test onQueryCanceled here because immediately canceling // the query will cause onResultsRequested not to be fired. See the next // test task for onQueryCanceled. }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("Test-private"); Assert.ok(provider); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: true, maxResults: 10, searchString: "*", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check isActive and priority. Assert.ok(provider.isActive(context)); Assert.equal(provider.getPriority(context), 0); // The events should have been fired. await Promise.all( ["onBehaviorRequested", "onResultsRequested"].map(msg => ext.awaitMessage(msg) ) ); await ext.unload(); }); // Same as the previous task but tests the onQueryCanceled event: Performs a // search in a private context for an extension that allows private browsing, // but cancels the search. The extension's onQueryCanceled listener should be // called. add_task(async function test_privateBrowsing_allowed_onQueryCanceled() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", background() { let name = "Test-private"; browser.urlbar.onBehaviorRequested.addListener(query => { browser.test.sendMessage("onBehaviorRequested"); return "active"; }, name); browser.urlbar.onQueryCanceled.addListener(query => { browser.test.sendMessage("onQueryCanceled"); }, name); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("Test-private"); Assert.ok(provider); // Run a query but immediately cancel it. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: true, maxResults: 10, searchString: "*", }); let controller = UrlbarTestUtils.newMockController(); let startPromise = controller.startQuery(context); controller.cancelQuery(); await startPromise; // onQueryCanceled should have been fired. await ext.awaitMessage("onQueryCanceled"); await ext.unload(); }); // Performs a search in a non-private context for an extension that does not // allow private browsing. The extension's listeners should be called. add_task(async function test_nonPrivateBrowsing() { let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], incognito: "not_allowed", }, isPrivileged: true, incognitoOverride: "spanning", background() { browser.urlbar.onBehaviorRequested.addListener(query => { return "active"; }, "test"); browser.urlbar.onResultsRequested.addListener(query => { return [ { type: "url", source: "history", payload: { title: "Test result", url: "https://example.com/", }, suggestedIndex: 1, }, ]; }, "test"); }, }); await ext.startup(); // Check the provider. let provider = UrlbarProvidersManager.getProvider("test"); Assert.ok(provider); // Run a query. let context = new UrlbarQueryContext({ allowAutofill: false, isPrivate: false, maxResults: 10, searchString: "test", }); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery(context); // Check isActive and priority. Assert.ok(provider.isActive(context)); Assert.equal(provider.getPriority(context), 0); // Check the results. Assert.equal(context.results.length, 2); Assert.ok(context.results[0].heuristic); Assert.equal(context.results[1].title, "Test result"); Assert.equal(context.results[1].suggestedIndex, 1); await ext.unload(); }); // Tests the engagementTelemetry property. add_task(async function test_engagementTelemetry() { let getPrefValue = () => UrlbarPrefs.get("eventTelemetry.enabled"); Assert.equal( getPrefValue(), false, "Engagement telemetry should be disabled by default" ); let ext = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["urlbar"], }, isPrivileged: true, incognitoOverride: "spanning", useAddonManager: "temporary", async background() { await browser.urlbar.engagementTelemetry.set({ value: true }); browser.test.sendMessage("ready"); }, }); await ext.startup(); await ext.awaitMessage("ready"); Assert.equal( getPrefValue(), true, "Successfully enabled the engagement telemetry" ); let completed = promiseUninstallCompleted(ext.id); await ext.unload(); await completed; Assert.equal( getPrefValue(), false, "Engagement telemetry should be reset after unloading the add-on" ); });