test_heuristic_cancel.js (7127B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * Tests that old results from UrlbarProviderAutofill do not overwrite results 7 * from UrlbarProviderHeuristicFallback after the autofillable query is 8 * cancelled. See bug 1653436. 9 */ 10 11 const { setTimeout } = ChromeUtils.importESModule( 12 "resource://gre/modules/Timer.sys.mjs" 13 ); 14 15 /** 16 * A test provider that waits before returning results to simulate a slow DB 17 * lookup. 18 */ 19 class SlowHeuristicProvider extends UrlbarTestUtils.TestProvider { 20 constructor({ results }) { 21 const delayResultsPromise = new Promise(resolve => 22 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 23 setTimeout(resolve, 300) 24 ); 25 super({ 26 name: "MyProvider", 27 type: UrlbarUtils.PROVIDER_TYPE.HEURISTIC, 28 results, 29 delayResultsPromise, 30 }); 31 } 32 } 33 34 /** 35 * A fast provider that alerts the test when it has added its results. 36 */ 37 class FastHeuristicProvider extends UrlbarTestUtils.TestProvider { 38 get type() { 39 return UrlbarUtils.PROVIDER_TYPE.HEURISTIC; 40 } 41 42 async startQuery(context, add) { 43 this._context = context; 44 for (let result of this.results) { 45 add(this, result); 46 } 47 Services.obs.notifyObservers(null, "results-added"); 48 } 49 } 50 51 add_setup(async function () { 52 registerCleanupFunction(async () => { 53 Services.prefs.clearUserPref("browser.urlbar.suggest.searches"); 54 }); 55 56 Services.prefs.setBoolPref("browser.urlbar.suggest.searches", false); 57 }); 58 59 /** 60 * Tests that UrlbarProvidersManager._heuristicProviderTimer is cancelled when 61 * a query is cancelled. 62 */ 63 add_task(async function timerIsCancelled() { 64 let context = createContext("m", { isPrivate: false }); 65 await PlacesTestUtils.promiseAsyncUpdates(); 66 info("Manually set up query and then overwrite it."); 67 // slowProvider is a stand-in for a slow UrlbarProviderPlaces returning a 68 // non-heuristic result. 69 let slowProvider = new SlowHeuristicProvider({ 70 results: [ 71 makeVisitResult(context, { 72 uri: `http://mozilla.org/`, 73 title: `mozilla.org/`, 74 }), 75 ], 76 }); 77 UrlbarProvidersManager.registerProvider(slowProvider); 78 79 // fastProvider is a stand-in for a fast Autofill returning a heuristic 80 // result. 81 let fastProvider = new FastHeuristicProvider({ 82 results: [ 83 makeVisitResult(context, { 84 uri: `http://mozilla.com/`, 85 title: `mozilla.com/`, 86 heuristic: true, 87 }), 88 ], 89 }); 90 UrlbarProvidersManager.registerProvider(fastProvider); 91 let firstContext = createContext("m", { 92 providers: [slowProvider.name, fastProvider.name], 93 }); 94 let secondContext = createContext("ma", { 95 providers: [slowProvider.name, fastProvider.name], 96 }); 97 98 let controller = UrlbarTestUtils.newMockController(); 99 let queryRecieved, queryCancelled; 100 const controllerListener = { 101 onQueryResults(queryContext) { 102 Assert.equal( 103 queryContext, 104 secondContext, 105 "Only the second query should finish." 106 ); 107 queryRecieved = true; 108 }, 109 onQueryCancelled(queryContext) { 110 Assert.equal( 111 queryContext, 112 firstContext, 113 "The first query should be cancelled." 114 ); 115 Assert.ok(!queryCancelled, "No more than one query should be cancelled."); 116 queryCancelled = true; 117 }, 118 }; 119 controller.addListener(controllerListener); 120 121 // Wait until FastProvider sends its results to the providers manager. 122 // Then they will be queued up in a _heuristicProvidersTimer, waiting for 123 // the results from SlowProvider. 124 let resultsAddedPromise = new Promise(resolve => { 125 let observe = async () => { 126 Services.obs.removeObserver(observe, "results-added"); 127 // Fire the second query to cancel the first. 128 await controller.startQuery(secondContext); 129 resolve(); 130 }; 131 132 Services.obs.addObserver(observe, "results-added"); 133 }); 134 135 controller.startQuery(firstContext); 136 await resultsAddedPromise; 137 138 Assert.ok(queryCancelled, "At least one query was cancelled."); 139 Assert.ok(queryRecieved, "At least one query finished."); 140 controller.removeListener(controllerListener); 141 }); 142 143 /** 144 * Tests that old autofill results aren't displayed after a query is cancelled. 145 * See bug 1653436. 146 */ 147 add_task(async function autofillIsCleared() { 148 /** 149 * Steps: 150 * 1. Start query. 151 * 2. Allow UrlbarProviderAutofill to start _getAutofillResult. 152 * 3. Execute a new query with no autofill match, cancelling the first 153 * query. 154 * 4. Test that the old result from UrlbarProviderAutofill isn't displayed. 155 */ 156 await PlacesTestUtils.addVisits("http://example.com"); 157 158 let firstContext = createContext("e", { 159 providers: ["UrlbarProviderAutofill", "UrlbarProviderHeuristicFallback"], 160 }); 161 let secondContext = createContext("em", { 162 providers: ["UrlbarProviderAutofill", "UrlbarProviderHeuristicFallback"], 163 }); 164 165 info("Sanity check: The first query autofills and the second does not."); 166 await check_results({ 167 firstContext, 168 autofilled: "example.com", 169 completed: "http://example.com/", 170 matches: [ 171 makeVisitResult(firstContext, { 172 uri: "http://example.com/", 173 title: "example.com", 174 heuristic: true, 175 }), 176 ], 177 }); 178 179 await check_results({ 180 secondContext, 181 matches: [ 182 makeSearchResult(secondContext, { 183 engineName: (await Services.search.getDefault()).name, 184 providerName: "UrlbarProviderHeuristicFallback", 185 heuristic: true, 186 }), 187 ], 188 }); 189 190 // Refresh our queries 191 firstContext = createContext("e", { 192 providers: ["UrlbarProviderAutofill", "UrlbarProviderHeuristicFallback"], 193 }); 194 secondContext = createContext("em", { 195 providers: ["UrlbarProviderAutofill", "UrlbarProviderHeuristicFallback"], 196 }); 197 198 // Set up controller to observe queries. 199 let controller = UrlbarTestUtils.newMockController(); 200 let queryRecieved, queryCancelled; 201 const controllerListener = { 202 onQueryResults(queryContext) { 203 Assert.equal( 204 queryContext, 205 secondContext, 206 "Only the second query should finish." 207 ); 208 queryRecieved = true; 209 }, 210 onQueryCancelled(queryContext) { 211 Assert.equal( 212 queryContext, 213 firstContext, 214 "The first query should be cancelled." 215 ); 216 Assert.ok( 217 !UrlbarProvidersManager.getProvider("UrlbarProviderAutofill") 218 ._autofillData, 219 "The first result should not have populated autofill data." 220 ); 221 Assert.ok(!queryCancelled, "No more than one query should be cancelled."); 222 queryCancelled = true; 223 }, 224 }; 225 controller.addListener(controllerListener); 226 227 // Intentionally do not await this first query. 228 controller.startQuery(firstContext); 229 await controller.startQuery(secondContext); 230 231 Assert.ok(queryCancelled, "At least one query was cancelled."); 232 Assert.ok(queryRecieved, "At least one query finished."); 233 controller.removeListener(controllerListener); 234 await cleanupPlaces(); 235 });