|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled"; |
|
5 const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directorySource"; |
|
6 |
|
7 Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true); |
|
8 // start with no directory links by default |
|
9 Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}"); |
|
10 |
|
11 let tmp = {}; |
|
12 Cu.import("resource://gre/modules/Promise.jsm", tmp); |
|
13 Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp); |
|
14 Cc["@mozilla.org/moz/jssubscript-loader;1"] |
|
15 .getService(Ci.mozIJSSubScriptLoader) |
|
16 .loadSubScript("chrome://browser/content/sanitize.js", tmp); |
|
17 let {Promise, NewTabUtils, Sanitizer} = tmp; |
|
18 |
|
19 let uri = Services.io.newURI("about:newtab", null, null); |
|
20 let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri); |
|
21 |
|
22 let isMac = ("nsILocalFileMac" in Ci); |
|
23 let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc); |
|
24 let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc); |
|
25 let gWindow = window; |
|
26 |
|
27 // The tests assume all three rows of sites are shown, but the window may be too |
|
28 // short to actually show three rows. Resize it if necessary. |
|
29 let requiredInnerHeight = |
|
30 40 + 32 + // undo container + bottom margin |
|
31 44 + 32 + // search bar + bottom margin |
|
32 (3 * (150 + 32)) + // 3 rows * (tile height + title and bottom margin) |
|
33 100; // breathing room |
|
34 |
|
35 let oldInnerHeight = null; |
|
36 if (gBrowser.contentWindow.innerHeight < requiredInnerHeight) { |
|
37 oldInnerHeight = gBrowser.contentWindow.innerHeight; |
|
38 info("Changing browser inner height from " + oldInnerHeight + " to " + |
|
39 requiredInnerHeight); |
|
40 gBrowser.contentWindow.innerHeight = requiredInnerHeight; |
|
41 let screenHeight = {}; |
|
42 Cc["@mozilla.org/gfx/screenmanager;1"]. |
|
43 getService(Ci.nsIScreenManager). |
|
44 primaryScreen. |
|
45 GetAvailRectDisplayPix({}, {}, {}, screenHeight); |
|
46 screenHeight = screenHeight.value; |
|
47 if (screenHeight < gBrowser.contentWindow.outerHeight) { |
|
48 info("Warning: Browser outer height is now " + |
|
49 gBrowser.contentWindow.outerHeight + ", which is larger than the " + |
|
50 "available screen height, " + screenHeight + |
|
51 ". That may cause problems."); |
|
52 } |
|
53 } |
|
54 |
|
55 registerCleanupFunction(function () { |
|
56 while (gWindow.gBrowser.tabs.length > 1) |
|
57 gWindow.gBrowser.removeTab(gWindow.gBrowser.tabs[1]); |
|
58 |
|
59 if (oldInnerHeight) |
|
60 gBrowser.contentWindow.innerHeight = oldInnerHeight; |
|
61 |
|
62 Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED); |
|
63 Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE); |
|
64 }); |
|
65 |
|
66 /** |
|
67 * Provide the default test function to start our test runner. |
|
68 */ |
|
69 function test() { |
|
70 TestRunner.run(); |
|
71 } |
|
72 |
|
73 /** |
|
74 * The test runner that controls the execution flow of our tests. |
|
75 */ |
|
76 let TestRunner = { |
|
77 /** |
|
78 * Starts the test runner. |
|
79 */ |
|
80 run: function () { |
|
81 waitForExplicitFinish(); |
|
82 |
|
83 this._iter = runTests(); |
|
84 this.next(); |
|
85 }, |
|
86 |
|
87 /** |
|
88 * Runs the next available test or finishes if there's no test left. |
|
89 */ |
|
90 next: function () { |
|
91 try { |
|
92 TestRunner._iter.next(); |
|
93 } catch (e if e instanceof StopIteration) { |
|
94 TestRunner.finish(); |
|
95 } |
|
96 }, |
|
97 |
|
98 /** |
|
99 * Finishes all tests and cleans up. |
|
100 */ |
|
101 finish: function () { |
|
102 function cleanupAndFinish() { |
|
103 clearHistory(function () { |
|
104 whenPagesUpdated(finish); |
|
105 NewTabUtils.restore(); |
|
106 }); |
|
107 } |
|
108 |
|
109 let callbacks = NewTabUtils.links._populateCallbacks; |
|
110 let numCallbacks = callbacks.length; |
|
111 |
|
112 if (numCallbacks) |
|
113 callbacks.splice(0, numCallbacks, cleanupAndFinish); |
|
114 else |
|
115 cleanupAndFinish(); |
|
116 } |
|
117 }; |
|
118 |
|
119 /** |
|
120 * Returns the selected tab's content window. |
|
121 * @return The content window. |
|
122 */ |
|
123 function getContentWindow() { |
|
124 return gWindow.gBrowser.selectedBrowser.contentWindow; |
|
125 } |
|
126 |
|
127 /** |
|
128 * Returns the selected tab's content document. |
|
129 * @return The content document. |
|
130 */ |
|
131 function getContentDocument() { |
|
132 return gWindow.gBrowser.selectedBrowser.contentDocument; |
|
133 } |
|
134 |
|
135 /** |
|
136 * Returns the newtab grid of the selected tab. |
|
137 * @return The newtab grid. |
|
138 */ |
|
139 function getGrid() { |
|
140 return getContentWindow().gGrid; |
|
141 } |
|
142 |
|
143 /** |
|
144 * Returns the cell at the given index of the selected tab's newtab grid. |
|
145 * @param aIndex The cell index. |
|
146 * @return The newtab cell. |
|
147 */ |
|
148 function getCell(aIndex) { |
|
149 return getGrid().cells[aIndex]; |
|
150 } |
|
151 |
|
152 /** |
|
153 * Allows to provide a list of links that is used to construct the grid. |
|
154 * @param aLinksPattern the pattern (see below) |
|
155 * |
|
156 * Example: setLinks("1,2,3") |
|
157 * Result: [{url: "http://example.com/#1", title: "site#1"}, |
|
158 * {url: "http://example.com/#2", title: "site#2"} |
|
159 * {url: "http://example.com/#3", title: "site#3"}] |
|
160 */ |
|
161 function setLinks(aLinks) { |
|
162 let links = aLinks; |
|
163 |
|
164 if (typeof links == "string") { |
|
165 links = aLinks.split(/\s*,\s*/).map(function (id) { |
|
166 return {url: "http://example.com/#" + id, title: "site#" + id}; |
|
167 }); |
|
168 } |
|
169 |
|
170 // Call populateCache() once to make sure that all link fetching that is |
|
171 // currently in progress has ended. We clear the history, fill it with the |
|
172 // given entries and call populateCache() now again to make sure the cache |
|
173 // has the desired contents. |
|
174 NewTabUtils.links.populateCache(function () { |
|
175 clearHistory(function () { |
|
176 fillHistory(links, function () { |
|
177 NewTabUtils.links.populateCache(function () { |
|
178 NewTabUtils.allPages.update(); |
|
179 TestRunner.next(); |
|
180 }, true); |
|
181 }); |
|
182 }); |
|
183 }); |
|
184 } |
|
185 |
|
186 function clearHistory(aCallback) { |
|
187 Services.obs.addObserver(function observe(aSubject, aTopic, aData) { |
|
188 Services.obs.removeObserver(observe, aTopic); |
|
189 executeSoon(aCallback); |
|
190 }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false); |
|
191 |
|
192 PlacesUtils.history.removeAllPages(); |
|
193 } |
|
194 |
|
195 function fillHistory(aLinks, aCallback) { |
|
196 let numLinks = aLinks.length; |
|
197 if (!numLinks) { |
|
198 if (aCallback) |
|
199 executeSoon(aCallback); |
|
200 return; |
|
201 } |
|
202 |
|
203 let transitionLink = Ci.nsINavHistoryService.TRANSITION_LINK; |
|
204 |
|
205 // Important: To avoid test failures due to clock jitter on Windows XP, call |
|
206 // Date.now() once here, not each time through the loop. |
|
207 let now = Date.now() * 1000; |
|
208 |
|
209 for (let i = 0; i < aLinks.length; i++) { |
|
210 let link = aLinks[i]; |
|
211 let place = { |
|
212 uri: makeURI(link.url), |
|
213 title: link.title, |
|
214 // Links are secondarily sorted by visit date descending, so decrease the |
|
215 // visit date as we progress through the array so that links appear in the |
|
216 // grid in the order they're present in the array. |
|
217 visits: [{visitDate: now - i, transitionType: transitionLink}] |
|
218 }; |
|
219 |
|
220 PlacesUtils.asyncHistory.updatePlaces(place, { |
|
221 handleError: function () ok(false, "couldn't add visit to history"), |
|
222 handleResult: function () {}, |
|
223 handleCompletion: function () { |
|
224 if (--numLinks == 0 && aCallback) |
|
225 aCallback(); |
|
226 } |
|
227 }); |
|
228 } |
|
229 } |
|
230 |
|
231 /** |
|
232 * Allows to specify the list of pinned links (that have a fixed position in |
|
233 * the grid. |
|
234 * @param aLinksPattern the pattern (see below) |
|
235 * |
|
236 * Example: setPinnedLinks("3,,1") |
|
237 * Result: 'http://example.com/#3' is pinned in the first cell. 'http://example.com/#1' is |
|
238 * pinned in the third cell. |
|
239 */ |
|
240 function setPinnedLinks(aLinks) { |
|
241 let links = aLinks; |
|
242 |
|
243 if (typeof links == "string") { |
|
244 links = aLinks.split(/\s*,\s*/).map(function (id) { |
|
245 if (id) |
|
246 return {url: "http://example.com/#" + id, title: "site#" + id}; |
|
247 }); |
|
248 } |
|
249 |
|
250 let string = Cc["@mozilla.org/supports-string;1"] |
|
251 .createInstance(Ci.nsISupportsString); |
|
252 string.data = JSON.stringify(links); |
|
253 Services.prefs.setComplexValue("browser.newtabpage.pinned", |
|
254 Ci.nsISupportsString, string); |
|
255 |
|
256 NewTabUtils.pinnedLinks.resetCache(); |
|
257 NewTabUtils.allPages.update(); |
|
258 } |
|
259 |
|
260 /** |
|
261 * Restore the grid state. |
|
262 */ |
|
263 function restore() { |
|
264 whenPagesUpdated(); |
|
265 NewTabUtils.restore(); |
|
266 } |
|
267 |
|
268 /** |
|
269 * Creates a new tab containing 'about:newtab'. |
|
270 */ |
|
271 function addNewTabPageTab() { |
|
272 let tab = gWindow.gBrowser.selectedTab = gWindow.gBrowser.addTab("about:newtab"); |
|
273 let browser = tab.linkedBrowser; |
|
274 |
|
275 function whenNewTabLoaded() { |
|
276 if (NewTabUtils.allPages.enabled) { |
|
277 // Continue when the link cache has been populated. |
|
278 NewTabUtils.links.populateCache(function () { |
|
279 executeSoon(TestRunner.next); |
|
280 }); |
|
281 } else { |
|
282 // It's important that we call next() asynchronously. |
|
283 // 'yield addNewTabPageTab()' would fail if next() is called |
|
284 // synchronously because the iterator is already executing. |
|
285 executeSoon(TestRunner.next); |
|
286 } |
|
287 } |
|
288 |
|
289 // The new tab page might have been preloaded in the background. |
|
290 if (browser.contentDocument.readyState == "complete") { |
|
291 whenNewTabLoaded(); |
|
292 return; |
|
293 } |
|
294 |
|
295 // Wait for the new tab page to be loaded. |
|
296 browser.addEventListener("load", function onLoad() { |
|
297 browser.removeEventListener("load", onLoad, true); |
|
298 whenNewTabLoaded(); |
|
299 }, true); |
|
300 } |
|
301 |
|
302 /** |
|
303 * Compares the current grid arrangement with the given pattern. |
|
304 * @param the pattern (see below) |
|
305 * @param the array of sites to compare with (optional) |
|
306 * |
|
307 * Example: checkGrid("3p,2,,1p") |
|
308 * Result: We expect the first cell to contain the pinned site 'http://example.com/#3'. |
|
309 * The second cell contains 'http://example.com/#2'. The third cell is empty. |
|
310 * The fourth cell contains the pinned site 'http://example.com/#4'. |
|
311 */ |
|
312 function checkGrid(aSitesPattern, aSites) { |
|
313 let length = aSitesPattern.split(",").length; |
|
314 let sites = (aSites || getGrid().sites).slice(0, length); |
|
315 let current = sites.map(function (aSite) { |
|
316 if (!aSite) |
|
317 return ""; |
|
318 |
|
319 let pinned = aSite.isPinned(); |
|
320 let pinButton = aSite.node.querySelector(".newtab-control-pin"); |
|
321 let hasPinnedAttr = pinButton.hasAttribute("pinned"); |
|
322 |
|
323 if (pinned != hasPinnedAttr) |
|
324 ok(false, "invalid state (site.isPinned() != site[pinned])"); |
|
325 |
|
326 return aSite.url.replace(/^http:\/\/example\.com\/#(\d+)$/, "$1") + (pinned ? "p" : ""); |
|
327 }); |
|
328 |
|
329 is(current, aSitesPattern, "grid status = " + aSitesPattern); |
|
330 } |
|
331 |
|
332 /** |
|
333 * Blocks a site from the grid. |
|
334 * @param aIndex The cell index. |
|
335 */ |
|
336 function blockCell(aIndex) { |
|
337 whenPagesUpdated(); |
|
338 getCell(aIndex).site.block(); |
|
339 } |
|
340 |
|
341 /** |
|
342 * Pins a site on a given position. |
|
343 * @param aIndex The cell index. |
|
344 * @param aPinIndex The index the defines where the site should be pinned. |
|
345 */ |
|
346 function pinCell(aIndex, aPinIndex) { |
|
347 getCell(aIndex).site.pin(aPinIndex); |
|
348 } |
|
349 |
|
350 /** |
|
351 * Unpins the given cell's site. |
|
352 * @param aIndex The cell index. |
|
353 */ |
|
354 function unpinCell(aIndex) { |
|
355 whenPagesUpdated(); |
|
356 getCell(aIndex).site.unpin(); |
|
357 } |
|
358 |
|
359 /** |
|
360 * Simulates a drag and drop operation. |
|
361 * @param aSourceIndex The cell index containing the dragged site. |
|
362 * @param aDestIndex The cell index of the drop target. |
|
363 */ |
|
364 function simulateDrop(aSourceIndex, aDestIndex) { |
|
365 let src = getCell(aSourceIndex).site.node; |
|
366 let dest = getCell(aDestIndex).node; |
|
367 |
|
368 // Drop 'src' onto 'dest' and continue testing when all newtab |
|
369 // pages have been updated (i.e. the drop operation is completed). |
|
370 startAndCompleteDragOperation(src, dest, whenPagesUpdated); |
|
371 } |
|
372 |
|
373 /** |
|
374 * Simulates a drag and drop operation. Instead of rearranging a site that is |
|
375 * is already contained in the newtab grid, this is used to simulate dragging |
|
376 * an external link onto the grid e.g. the text from the URL bar. |
|
377 * @param aDestIndex The cell index of the drop target. |
|
378 */ |
|
379 function simulateExternalDrop(aDestIndex) { |
|
380 let dest = getCell(aDestIndex).node; |
|
381 |
|
382 // Create an iframe that contains the external link we'll drag. |
|
383 createExternalDropIframe().then(iframe => { |
|
384 let link = iframe.contentDocument.getElementById("link"); |
|
385 |
|
386 // Drop 'link' onto 'dest'. |
|
387 startAndCompleteDragOperation(link, dest, () => { |
|
388 // Wait until the drop operation is complete |
|
389 // and all newtab pages have been updated. |
|
390 whenPagesUpdated(() => { |
|
391 // Clean up and remove the iframe. |
|
392 iframe.remove(); |
|
393 // Continue testing. |
|
394 TestRunner.next(); |
|
395 }); |
|
396 }); |
|
397 }); |
|
398 } |
|
399 |
|
400 /** |
|
401 * Starts and complete a drag-and-drop operation. |
|
402 * @param aSource The node that is being dragged. |
|
403 * @param aDest The node we're dragging aSource onto. |
|
404 * @param aCallback The function that is called when we're done. |
|
405 */ |
|
406 function startAndCompleteDragOperation(aSource, aDest, aCallback) { |
|
407 // Start by pressing the left mouse button. |
|
408 synthesizeNativeMouseLDown(aSource); |
|
409 |
|
410 // Move the mouse in 5px steps until the drag operation starts. |
|
411 let offset = 0; |
|
412 let interval = setInterval(() => { |
|
413 synthesizeNativeMouseDrag(aSource, offset += 5); |
|
414 }, 10); |
|
415 |
|
416 // When the drag operation has started we'll move |
|
417 // the dragged element to its target position. |
|
418 aSource.addEventListener("dragstart", function onDragStart() { |
|
419 aSource.removeEventListener("dragstart", onDragStart); |
|
420 clearInterval(interval); |
|
421 |
|
422 // Place the cursor above the drag target. |
|
423 synthesizeNativeMouseMove(aDest); |
|
424 }); |
|
425 |
|
426 // As soon as the dragged element hovers the target, we'll drop it. |
|
427 aDest.addEventListener("dragenter", function onDragEnter() { |
|
428 aDest.removeEventListener("dragenter", onDragEnter); |
|
429 |
|
430 // Finish the drop operation. |
|
431 synthesizeNativeMouseLUp(aDest); |
|
432 aCallback(); |
|
433 }); |
|
434 } |
|
435 |
|
436 /** |
|
437 * Helper function that creates a temporary iframe in the about:newtab |
|
438 * document. This will contain a link we can drag to the test the dropping |
|
439 * of links from external documents. |
|
440 */ |
|
441 function createExternalDropIframe() { |
|
442 const url = "data:text/html;charset=utf-8," + |
|
443 "<a id='link' href='http://example.com/%2399'>link</a>"; |
|
444 |
|
445 let deferred = Promise.defer(); |
|
446 let doc = getContentDocument(); |
|
447 let iframe = doc.createElement("iframe"); |
|
448 iframe.setAttribute("src", url); |
|
449 iframe.style.width = "50px"; |
|
450 iframe.style.height = "50px"; |
|
451 |
|
452 let margin = doc.getElementById("newtab-margin-top"); |
|
453 margin.appendChild(iframe); |
|
454 |
|
455 iframe.addEventListener("load", function onLoad() { |
|
456 iframe.removeEventListener("load", onLoad); |
|
457 executeSoon(() => deferred.resolve(iframe)); |
|
458 }); |
|
459 |
|
460 return deferred.promise; |
|
461 } |
|
462 |
|
463 /** |
|
464 * Fires a synthetic 'mousedown' event on the current about:newtab page. |
|
465 * @param aElement The element used to determine the cursor position. |
|
466 */ |
|
467 function synthesizeNativeMouseLDown(aElement) { |
|
468 if (isLinux) { |
|
469 let win = aElement.ownerDocument.defaultView; |
|
470 EventUtils.synthesizeMouseAtCenter(aElement, {type: "mousedown"}, win); |
|
471 } else { |
|
472 let msg = isWindows ? 2 : 1; |
|
473 synthesizeNativeMouseEvent(aElement, msg); |
|
474 } |
|
475 } |
|
476 |
|
477 /** |
|
478 * Fires a synthetic 'mouseup' event on the current about:newtab page. |
|
479 * @param aElement The element used to determine the cursor position. |
|
480 */ |
|
481 function synthesizeNativeMouseLUp(aElement) { |
|
482 let msg = isWindows ? 4 : (isMac ? 2 : 7); |
|
483 synthesizeNativeMouseEvent(aElement, msg); |
|
484 } |
|
485 |
|
486 /** |
|
487 * Fires a synthetic mouse drag event on the current about:newtab page. |
|
488 * @param aElement The element used to determine the cursor position. |
|
489 * @param aOffsetX The left offset that is added to the position. |
|
490 */ |
|
491 function synthesizeNativeMouseDrag(aElement, aOffsetX) { |
|
492 let msg = isMac ? 6 : 1; |
|
493 synthesizeNativeMouseEvent(aElement, msg, aOffsetX); |
|
494 } |
|
495 |
|
496 /** |
|
497 * Fires a synthetic 'mousemove' event on the current about:newtab page. |
|
498 * @param aElement The element used to determine the cursor position. |
|
499 */ |
|
500 function synthesizeNativeMouseMove(aElement) { |
|
501 let msg = isMac ? 5 : 1; |
|
502 synthesizeNativeMouseEvent(aElement, msg); |
|
503 } |
|
504 |
|
505 /** |
|
506 * Fires a synthetic mouse event on the current about:newtab page. |
|
507 * @param aElement The element used to determine the cursor position. |
|
508 * @param aOffsetX The left offset that is added to the position (optional). |
|
509 * @param aOffsetY The top offset that is added to the position (optional). |
|
510 */ |
|
511 function synthesizeNativeMouseEvent(aElement, aMsg, aOffsetX = 0, aOffsetY = 0) { |
|
512 let rect = aElement.getBoundingClientRect(); |
|
513 let win = aElement.ownerDocument.defaultView; |
|
514 let x = aOffsetX + win.mozInnerScreenX + rect.left + rect.width / 2; |
|
515 let y = aOffsetY + win.mozInnerScreenY + rect.top + rect.height / 2; |
|
516 |
|
517 let utils = win.QueryInterface(Ci.nsIInterfaceRequestor) |
|
518 .getInterface(Ci.nsIDOMWindowUtils); |
|
519 |
|
520 let scale = utils.screenPixelsPerCSSPixel; |
|
521 utils.sendNativeMouseEvent(x * scale, y * scale, aMsg, 0, null); |
|
522 } |
|
523 |
|
524 /** |
|
525 * Sends a custom drag event to a given DOM element. |
|
526 * @param aEventType The drag event's type. |
|
527 * @param aTarget The DOM element that the event is dispatched to. |
|
528 * @param aData The event's drag data (optional). |
|
529 */ |
|
530 function sendDragEvent(aEventType, aTarget, aData) { |
|
531 let event = createDragEvent(aEventType, aData); |
|
532 let ifaceReq = getContentWindow().QueryInterface(Ci.nsIInterfaceRequestor); |
|
533 let windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils); |
|
534 windowUtils.dispatchDOMEventViaPresShell(aTarget, event, true); |
|
535 } |
|
536 |
|
537 /** |
|
538 * Creates a custom drag event. |
|
539 * @param aEventType The drag event's type. |
|
540 * @param aData The event's drag data (optional). |
|
541 * @return The drag event. |
|
542 */ |
|
543 function createDragEvent(aEventType, aData) { |
|
544 let dataTransfer = new (getContentWindow()).DataTransfer("dragstart", false); |
|
545 dataTransfer.mozSetDataAt("text/x-moz-url", aData, 0); |
|
546 let event = getContentDocument().createEvent("DragEvents"); |
|
547 event.initDragEvent(aEventType, true, true, getContentWindow(), 0, 0, 0, 0, 0, |
|
548 false, false, false, false, 0, null, dataTransfer); |
|
549 |
|
550 return event; |
|
551 } |
|
552 |
|
553 /** |
|
554 * Resumes testing when all pages have been updated. |
|
555 * @param aCallback Called when done. If not specified, TestRunner.next is used. |
|
556 * @param aOnlyIfHidden If true, this resumes testing only when an update that |
|
557 * applies to pre-loaded, hidden pages is observed. If |
|
558 * false, this resumes testing when any update is observed. |
|
559 */ |
|
560 function whenPagesUpdated(aCallback, aOnlyIfHidden=false) { |
|
561 let page = { |
|
562 update: function (onlyIfHidden=false) { |
|
563 if (onlyIfHidden == aOnlyIfHidden) { |
|
564 NewTabUtils.allPages.unregister(this); |
|
565 executeSoon(aCallback || TestRunner.next); |
|
566 } |
|
567 } |
|
568 }; |
|
569 |
|
570 NewTabUtils.allPages.register(page); |
|
571 registerCleanupFunction(function () { |
|
572 NewTabUtils.allPages.unregister(page); |
|
573 }); |
|
574 } |
|
575 |
|
576 /** |
|
577 * Waits a small amount of time for search events to stop occurring in the |
|
578 * newtab page. |
|
579 * |
|
580 * newtab pages receive some search events around load time that are difficult |
|
581 * to predict. There are two categories of such events: (1) "State" events |
|
582 * triggered by engine notifications like engine-changed, due to the search |
|
583 * service initializing itself on app startup. This can happen when a test is |
|
584 * the first test to run. (2) "State" events triggered by the newtab page |
|
585 * itself when gSearch first sets itself up. newtab preloading makes these a |
|
586 * pain to predict. |
|
587 */ |
|
588 function whenSearchInitDone() { |
|
589 info("Waiting for initial search events..."); |
|
590 let numTicks = 0; |
|
591 function reset(event) { |
|
592 info("Got initial search event " + event.detail.type + |
|
593 ", waiting for more..."); |
|
594 numTicks = 0; |
|
595 } |
|
596 let eventName = "ContentSearchService"; |
|
597 getContentWindow().addEventListener(eventName, reset); |
|
598 let interval = window.setInterval(() => { |
|
599 if (++numTicks >= 100) { |
|
600 info("Done waiting for initial search events"); |
|
601 window.clearInterval(interval); |
|
602 getContentWindow().removeEventListener(eventName, reset); |
|
603 TestRunner.next(); |
|
604 } |
|
605 }, 0); |
|
606 } |