1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/base/content/test/newtab/head.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,606 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled"; 1.8 +const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directorySource"; 1.9 + 1.10 +Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true); 1.11 +// start with no directory links by default 1.12 +Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}"); 1.13 + 1.14 +let tmp = {}; 1.15 +Cu.import("resource://gre/modules/Promise.jsm", tmp); 1.16 +Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp); 1.17 +Cc["@mozilla.org/moz/jssubscript-loader;1"] 1.18 + .getService(Ci.mozIJSSubScriptLoader) 1.19 + .loadSubScript("chrome://browser/content/sanitize.js", tmp); 1.20 +let {Promise, NewTabUtils, Sanitizer} = tmp; 1.21 + 1.22 +let uri = Services.io.newURI("about:newtab", null, null); 1.23 +let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri); 1.24 + 1.25 +let isMac = ("nsILocalFileMac" in Ci); 1.26 +let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc); 1.27 +let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc); 1.28 +let gWindow = window; 1.29 + 1.30 +// The tests assume all three rows of sites are shown, but the window may be too 1.31 +// short to actually show three rows. Resize it if necessary. 1.32 +let requiredInnerHeight = 1.33 + 40 + 32 + // undo container + bottom margin 1.34 + 44 + 32 + // search bar + bottom margin 1.35 + (3 * (150 + 32)) + // 3 rows * (tile height + title and bottom margin) 1.36 + 100; // breathing room 1.37 + 1.38 +let oldInnerHeight = null; 1.39 +if (gBrowser.contentWindow.innerHeight < requiredInnerHeight) { 1.40 + oldInnerHeight = gBrowser.contentWindow.innerHeight; 1.41 + info("Changing browser inner height from " + oldInnerHeight + " to " + 1.42 + requiredInnerHeight); 1.43 + gBrowser.contentWindow.innerHeight = requiredInnerHeight; 1.44 + let screenHeight = {}; 1.45 + Cc["@mozilla.org/gfx/screenmanager;1"]. 1.46 + getService(Ci.nsIScreenManager). 1.47 + primaryScreen. 1.48 + GetAvailRectDisplayPix({}, {}, {}, screenHeight); 1.49 + screenHeight = screenHeight.value; 1.50 + if (screenHeight < gBrowser.contentWindow.outerHeight) { 1.51 + info("Warning: Browser outer height is now " + 1.52 + gBrowser.contentWindow.outerHeight + ", which is larger than the " + 1.53 + "available screen height, " + screenHeight + 1.54 + ". That may cause problems."); 1.55 + } 1.56 +} 1.57 + 1.58 +registerCleanupFunction(function () { 1.59 + while (gWindow.gBrowser.tabs.length > 1) 1.60 + gWindow.gBrowser.removeTab(gWindow.gBrowser.tabs[1]); 1.61 + 1.62 + if (oldInnerHeight) 1.63 + gBrowser.contentWindow.innerHeight = oldInnerHeight; 1.64 + 1.65 + Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED); 1.66 + Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE); 1.67 +}); 1.68 + 1.69 +/** 1.70 + * Provide the default test function to start our test runner. 1.71 + */ 1.72 +function test() { 1.73 + TestRunner.run(); 1.74 +} 1.75 + 1.76 +/** 1.77 + * The test runner that controls the execution flow of our tests. 1.78 + */ 1.79 +let TestRunner = { 1.80 + /** 1.81 + * Starts the test runner. 1.82 + */ 1.83 + run: function () { 1.84 + waitForExplicitFinish(); 1.85 + 1.86 + this._iter = runTests(); 1.87 + this.next(); 1.88 + }, 1.89 + 1.90 + /** 1.91 + * Runs the next available test or finishes if there's no test left. 1.92 + */ 1.93 + next: function () { 1.94 + try { 1.95 + TestRunner._iter.next(); 1.96 + } catch (e if e instanceof StopIteration) { 1.97 + TestRunner.finish(); 1.98 + } 1.99 + }, 1.100 + 1.101 + /** 1.102 + * Finishes all tests and cleans up. 1.103 + */ 1.104 + finish: function () { 1.105 + function cleanupAndFinish() { 1.106 + clearHistory(function () { 1.107 + whenPagesUpdated(finish); 1.108 + NewTabUtils.restore(); 1.109 + }); 1.110 + } 1.111 + 1.112 + let callbacks = NewTabUtils.links._populateCallbacks; 1.113 + let numCallbacks = callbacks.length; 1.114 + 1.115 + if (numCallbacks) 1.116 + callbacks.splice(0, numCallbacks, cleanupAndFinish); 1.117 + else 1.118 + cleanupAndFinish(); 1.119 + } 1.120 +}; 1.121 + 1.122 +/** 1.123 + * Returns the selected tab's content window. 1.124 + * @return The content window. 1.125 + */ 1.126 +function getContentWindow() { 1.127 + return gWindow.gBrowser.selectedBrowser.contentWindow; 1.128 +} 1.129 + 1.130 +/** 1.131 + * Returns the selected tab's content document. 1.132 + * @return The content document. 1.133 + */ 1.134 +function getContentDocument() { 1.135 + return gWindow.gBrowser.selectedBrowser.contentDocument; 1.136 +} 1.137 + 1.138 +/** 1.139 + * Returns the newtab grid of the selected tab. 1.140 + * @return The newtab grid. 1.141 + */ 1.142 +function getGrid() { 1.143 + return getContentWindow().gGrid; 1.144 +} 1.145 + 1.146 +/** 1.147 + * Returns the cell at the given index of the selected tab's newtab grid. 1.148 + * @param aIndex The cell index. 1.149 + * @return The newtab cell. 1.150 + */ 1.151 +function getCell(aIndex) { 1.152 + return getGrid().cells[aIndex]; 1.153 +} 1.154 + 1.155 +/** 1.156 + * Allows to provide a list of links that is used to construct the grid. 1.157 + * @param aLinksPattern the pattern (see below) 1.158 + * 1.159 + * Example: setLinks("1,2,3") 1.160 + * Result: [{url: "http://example.com/#1", title: "site#1"}, 1.161 + * {url: "http://example.com/#2", title: "site#2"} 1.162 + * {url: "http://example.com/#3", title: "site#3"}] 1.163 + */ 1.164 +function setLinks(aLinks) { 1.165 + let links = aLinks; 1.166 + 1.167 + if (typeof links == "string") { 1.168 + links = aLinks.split(/\s*,\s*/).map(function (id) { 1.169 + return {url: "http://example.com/#" + id, title: "site#" + id}; 1.170 + }); 1.171 + } 1.172 + 1.173 + // Call populateCache() once to make sure that all link fetching that is 1.174 + // currently in progress has ended. We clear the history, fill it with the 1.175 + // given entries and call populateCache() now again to make sure the cache 1.176 + // has the desired contents. 1.177 + NewTabUtils.links.populateCache(function () { 1.178 + clearHistory(function () { 1.179 + fillHistory(links, function () { 1.180 + NewTabUtils.links.populateCache(function () { 1.181 + NewTabUtils.allPages.update(); 1.182 + TestRunner.next(); 1.183 + }, true); 1.184 + }); 1.185 + }); 1.186 + }); 1.187 +} 1.188 + 1.189 +function clearHistory(aCallback) { 1.190 + Services.obs.addObserver(function observe(aSubject, aTopic, aData) { 1.191 + Services.obs.removeObserver(observe, aTopic); 1.192 + executeSoon(aCallback); 1.193 + }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false); 1.194 + 1.195 + PlacesUtils.history.removeAllPages(); 1.196 +} 1.197 + 1.198 +function fillHistory(aLinks, aCallback) { 1.199 + let numLinks = aLinks.length; 1.200 + if (!numLinks) { 1.201 + if (aCallback) 1.202 + executeSoon(aCallback); 1.203 + return; 1.204 + } 1.205 + 1.206 + let transitionLink = Ci.nsINavHistoryService.TRANSITION_LINK; 1.207 + 1.208 + // Important: To avoid test failures due to clock jitter on Windows XP, call 1.209 + // Date.now() once here, not each time through the loop. 1.210 + let now = Date.now() * 1000; 1.211 + 1.212 + for (let i = 0; i < aLinks.length; i++) { 1.213 + let link = aLinks[i]; 1.214 + let place = { 1.215 + uri: makeURI(link.url), 1.216 + title: link.title, 1.217 + // Links are secondarily sorted by visit date descending, so decrease the 1.218 + // visit date as we progress through the array so that links appear in the 1.219 + // grid in the order they're present in the array. 1.220 + visits: [{visitDate: now - i, transitionType: transitionLink}] 1.221 + }; 1.222 + 1.223 + PlacesUtils.asyncHistory.updatePlaces(place, { 1.224 + handleError: function () ok(false, "couldn't add visit to history"), 1.225 + handleResult: function () {}, 1.226 + handleCompletion: function () { 1.227 + if (--numLinks == 0 && aCallback) 1.228 + aCallback(); 1.229 + } 1.230 + }); 1.231 + } 1.232 +} 1.233 + 1.234 +/** 1.235 + * Allows to specify the list of pinned links (that have a fixed position in 1.236 + * the grid. 1.237 + * @param aLinksPattern the pattern (see below) 1.238 + * 1.239 + * Example: setPinnedLinks("3,,1") 1.240 + * Result: 'http://example.com/#3' is pinned in the first cell. 'http://example.com/#1' is 1.241 + * pinned in the third cell. 1.242 + */ 1.243 +function setPinnedLinks(aLinks) { 1.244 + let links = aLinks; 1.245 + 1.246 + if (typeof links == "string") { 1.247 + links = aLinks.split(/\s*,\s*/).map(function (id) { 1.248 + if (id) 1.249 + return {url: "http://example.com/#" + id, title: "site#" + id}; 1.250 + }); 1.251 + } 1.252 + 1.253 + let string = Cc["@mozilla.org/supports-string;1"] 1.254 + .createInstance(Ci.nsISupportsString); 1.255 + string.data = JSON.stringify(links); 1.256 + Services.prefs.setComplexValue("browser.newtabpage.pinned", 1.257 + Ci.nsISupportsString, string); 1.258 + 1.259 + NewTabUtils.pinnedLinks.resetCache(); 1.260 + NewTabUtils.allPages.update(); 1.261 +} 1.262 + 1.263 +/** 1.264 + * Restore the grid state. 1.265 + */ 1.266 +function restore() { 1.267 + whenPagesUpdated(); 1.268 + NewTabUtils.restore(); 1.269 +} 1.270 + 1.271 +/** 1.272 + * Creates a new tab containing 'about:newtab'. 1.273 + */ 1.274 +function addNewTabPageTab() { 1.275 + let tab = gWindow.gBrowser.selectedTab = gWindow.gBrowser.addTab("about:newtab"); 1.276 + let browser = tab.linkedBrowser; 1.277 + 1.278 + function whenNewTabLoaded() { 1.279 + if (NewTabUtils.allPages.enabled) { 1.280 + // Continue when the link cache has been populated. 1.281 + NewTabUtils.links.populateCache(function () { 1.282 + executeSoon(TestRunner.next); 1.283 + }); 1.284 + } else { 1.285 + // It's important that we call next() asynchronously. 1.286 + // 'yield addNewTabPageTab()' would fail if next() is called 1.287 + // synchronously because the iterator is already executing. 1.288 + executeSoon(TestRunner.next); 1.289 + } 1.290 + } 1.291 + 1.292 + // The new tab page might have been preloaded in the background. 1.293 + if (browser.contentDocument.readyState == "complete") { 1.294 + whenNewTabLoaded(); 1.295 + return; 1.296 + } 1.297 + 1.298 + // Wait for the new tab page to be loaded. 1.299 + browser.addEventListener("load", function onLoad() { 1.300 + browser.removeEventListener("load", onLoad, true); 1.301 + whenNewTabLoaded(); 1.302 + }, true); 1.303 +} 1.304 + 1.305 +/** 1.306 + * Compares the current grid arrangement with the given pattern. 1.307 + * @param the pattern (see below) 1.308 + * @param the array of sites to compare with (optional) 1.309 + * 1.310 + * Example: checkGrid("3p,2,,1p") 1.311 + * Result: We expect the first cell to contain the pinned site 'http://example.com/#3'. 1.312 + * The second cell contains 'http://example.com/#2'. The third cell is empty. 1.313 + * The fourth cell contains the pinned site 'http://example.com/#4'. 1.314 + */ 1.315 +function checkGrid(aSitesPattern, aSites) { 1.316 + let length = aSitesPattern.split(",").length; 1.317 + let sites = (aSites || getGrid().sites).slice(0, length); 1.318 + let current = sites.map(function (aSite) { 1.319 + if (!aSite) 1.320 + return ""; 1.321 + 1.322 + let pinned = aSite.isPinned(); 1.323 + let pinButton = aSite.node.querySelector(".newtab-control-pin"); 1.324 + let hasPinnedAttr = pinButton.hasAttribute("pinned"); 1.325 + 1.326 + if (pinned != hasPinnedAttr) 1.327 + ok(false, "invalid state (site.isPinned() != site[pinned])"); 1.328 + 1.329 + return aSite.url.replace(/^http:\/\/example\.com\/#(\d+)$/, "$1") + (pinned ? "p" : ""); 1.330 + }); 1.331 + 1.332 + is(current, aSitesPattern, "grid status = " + aSitesPattern); 1.333 +} 1.334 + 1.335 +/** 1.336 + * Blocks a site from the grid. 1.337 + * @param aIndex The cell index. 1.338 + */ 1.339 +function blockCell(aIndex) { 1.340 + whenPagesUpdated(); 1.341 + getCell(aIndex).site.block(); 1.342 +} 1.343 + 1.344 +/** 1.345 + * Pins a site on a given position. 1.346 + * @param aIndex The cell index. 1.347 + * @param aPinIndex The index the defines where the site should be pinned. 1.348 + */ 1.349 +function pinCell(aIndex, aPinIndex) { 1.350 + getCell(aIndex).site.pin(aPinIndex); 1.351 +} 1.352 + 1.353 +/** 1.354 + * Unpins the given cell's site. 1.355 + * @param aIndex The cell index. 1.356 + */ 1.357 +function unpinCell(aIndex) { 1.358 + whenPagesUpdated(); 1.359 + getCell(aIndex).site.unpin(); 1.360 +} 1.361 + 1.362 +/** 1.363 + * Simulates a drag and drop operation. 1.364 + * @param aSourceIndex The cell index containing the dragged site. 1.365 + * @param aDestIndex The cell index of the drop target. 1.366 + */ 1.367 +function simulateDrop(aSourceIndex, aDestIndex) { 1.368 + let src = getCell(aSourceIndex).site.node; 1.369 + let dest = getCell(aDestIndex).node; 1.370 + 1.371 + // Drop 'src' onto 'dest' and continue testing when all newtab 1.372 + // pages have been updated (i.e. the drop operation is completed). 1.373 + startAndCompleteDragOperation(src, dest, whenPagesUpdated); 1.374 +} 1.375 + 1.376 +/** 1.377 + * Simulates a drag and drop operation. Instead of rearranging a site that is 1.378 + * is already contained in the newtab grid, this is used to simulate dragging 1.379 + * an external link onto the grid e.g. the text from the URL bar. 1.380 + * @param aDestIndex The cell index of the drop target. 1.381 + */ 1.382 +function simulateExternalDrop(aDestIndex) { 1.383 + let dest = getCell(aDestIndex).node; 1.384 + 1.385 + // Create an iframe that contains the external link we'll drag. 1.386 + createExternalDropIframe().then(iframe => { 1.387 + let link = iframe.contentDocument.getElementById("link"); 1.388 + 1.389 + // Drop 'link' onto 'dest'. 1.390 + startAndCompleteDragOperation(link, dest, () => { 1.391 + // Wait until the drop operation is complete 1.392 + // and all newtab pages have been updated. 1.393 + whenPagesUpdated(() => { 1.394 + // Clean up and remove the iframe. 1.395 + iframe.remove(); 1.396 + // Continue testing. 1.397 + TestRunner.next(); 1.398 + }); 1.399 + }); 1.400 + }); 1.401 +} 1.402 + 1.403 +/** 1.404 + * Starts and complete a drag-and-drop operation. 1.405 + * @param aSource The node that is being dragged. 1.406 + * @param aDest The node we're dragging aSource onto. 1.407 + * @param aCallback The function that is called when we're done. 1.408 + */ 1.409 +function startAndCompleteDragOperation(aSource, aDest, aCallback) { 1.410 + // Start by pressing the left mouse button. 1.411 + synthesizeNativeMouseLDown(aSource); 1.412 + 1.413 + // Move the mouse in 5px steps until the drag operation starts. 1.414 + let offset = 0; 1.415 + let interval = setInterval(() => { 1.416 + synthesizeNativeMouseDrag(aSource, offset += 5); 1.417 + }, 10); 1.418 + 1.419 + // When the drag operation has started we'll move 1.420 + // the dragged element to its target position. 1.421 + aSource.addEventListener("dragstart", function onDragStart() { 1.422 + aSource.removeEventListener("dragstart", onDragStart); 1.423 + clearInterval(interval); 1.424 + 1.425 + // Place the cursor above the drag target. 1.426 + synthesizeNativeMouseMove(aDest); 1.427 + }); 1.428 + 1.429 + // As soon as the dragged element hovers the target, we'll drop it. 1.430 + aDest.addEventListener("dragenter", function onDragEnter() { 1.431 + aDest.removeEventListener("dragenter", onDragEnter); 1.432 + 1.433 + // Finish the drop operation. 1.434 + synthesizeNativeMouseLUp(aDest); 1.435 + aCallback(); 1.436 + }); 1.437 +} 1.438 + 1.439 +/** 1.440 + * Helper function that creates a temporary iframe in the about:newtab 1.441 + * document. This will contain a link we can drag to the test the dropping 1.442 + * of links from external documents. 1.443 + */ 1.444 +function createExternalDropIframe() { 1.445 + const url = "data:text/html;charset=utf-8," + 1.446 + "<a id='link' href='http://example.com/%2399'>link</a>"; 1.447 + 1.448 + let deferred = Promise.defer(); 1.449 + let doc = getContentDocument(); 1.450 + let iframe = doc.createElement("iframe"); 1.451 + iframe.setAttribute("src", url); 1.452 + iframe.style.width = "50px"; 1.453 + iframe.style.height = "50px"; 1.454 + 1.455 + let margin = doc.getElementById("newtab-margin-top"); 1.456 + margin.appendChild(iframe); 1.457 + 1.458 + iframe.addEventListener("load", function onLoad() { 1.459 + iframe.removeEventListener("load", onLoad); 1.460 + executeSoon(() => deferred.resolve(iframe)); 1.461 + }); 1.462 + 1.463 + return deferred.promise; 1.464 +} 1.465 + 1.466 +/** 1.467 + * Fires a synthetic 'mousedown' event on the current about:newtab page. 1.468 + * @param aElement The element used to determine the cursor position. 1.469 + */ 1.470 +function synthesizeNativeMouseLDown(aElement) { 1.471 + if (isLinux) { 1.472 + let win = aElement.ownerDocument.defaultView; 1.473 + EventUtils.synthesizeMouseAtCenter(aElement, {type: "mousedown"}, win); 1.474 + } else { 1.475 + let msg = isWindows ? 2 : 1; 1.476 + synthesizeNativeMouseEvent(aElement, msg); 1.477 + } 1.478 +} 1.479 + 1.480 +/** 1.481 + * Fires a synthetic 'mouseup' event on the current about:newtab page. 1.482 + * @param aElement The element used to determine the cursor position. 1.483 + */ 1.484 +function synthesizeNativeMouseLUp(aElement) { 1.485 + let msg = isWindows ? 4 : (isMac ? 2 : 7); 1.486 + synthesizeNativeMouseEvent(aElement, msg); 1.487 +} 1.488 + 1.489 +/** 1.490 + * Fires a synthetic mouse drag event on the current about:newtab page. 1.491 + * @param aElement The element used to determine the cursor position. 1.492 + * @param aOffsetX The left offset that is added to the position. 1.493 + */ 1.494 +function synthesizeNativeMouseDrag(aElement, aOffsetX) { 1.495 + let msg = isMac ? 6 : 1; 1.496 + synthesizeNativeMouseEvent(aElement, msg, aOffsetX); 1.497 +} 1.498 + 1.499 +/** 1.500 + * Fires a synthetic 'mousemove' event on the current about:newtab page. 1.501 + * @param aElement The element used to determine the cursor position. 1.502 + */ 1.503 +function synthesizeNativeMouseMove(aElement) { 1.504 + let msg = isMac ? 5 : 1; 1.505 + synthesizeNativeMouseEvent(aElement, msg); 1.506 +} 1.507 + 1.508 +/** 1.509 + * Fires a synthetic mouse event on the current about:newtab page. 1.510 + * @param aElement The element used to determine the cursor position. 1.511 + * @param aOffsetX The left offset that is added to the position (optional). 1.512 + * @param aOffsetY The top offset that is added to the position (optional). 1.513 + */ 1.514 +function synthesizeNativeMouseEvent(aElement, aMsg, aOffsetX = 0, aOffsetY = 0) { 1.515 + let rect = aElement.getBoundingClientRect(); 1.516 + let win = aElement.ownerDocument.defaultView; 1.517 + let x = aOffsetX + win.mozInnerScreenX + rect.left + rect.width / 2; 1.518 + let y = aOffsetY + win.mozInnerScreenY + rect.top + rect.height / 2; 1.519 + 1.520 + let utils = win.QueryInterface(Ci.nsIInterfaceRequestor) 1.521 + .getInterface(Ci.nsIDOMWindowUtils); 1.522 + 1.523 + let scale = utils.screenPixelsPerCSSPixel; 1.524 + utils.sendNativeMouseEvent(x * scale, y * scale, aMsg, 0, null); 1.525 +} 1.526 + 1.527 +/** 1.528 + * Sends a custom drag event to a given DOM element. 1.529 + * @param aEventType The drag event's type. 1.530 + * @param aTarget The DOM element that the event is dispatched to. 1.531 + * @param aData The event's drag data (optional). 1.532 + */ 1.533 +function sendDragEvent(aEventType, aTarget, aData) { 1.534 + let event = createDragEvent(aEventType, aData); 1.535 + let ifaceReq = getContentWindow().QueryInterface(Ci.nsIInterfaceRequestor); 1.536 + let windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils); 1.537 + windowUtils.dispatchDOMEventViaPresShell(aTarget, event, true); 1.538 +} 1.539 + 1.540 +/** 1.541 + * Creates a custom drag event. 1.542 + * @param aEventType The drag event's type. 1.543 + * @param aData The event's drag data (optional). 1.544 + * @return The drag event. 1.545 + */ 1.546 +function createDragEvent(aEventType, aData) { 1.547 + let dataTransfer = new (getContentWindow()).DataTransfer("dragstart", false); 1.548 + dataTransfer.mozSetDataAt("text/x-moz-url", aData, 0); 1.549 + let event = getContentDocument().createEvent("DragEvents"); 1.550 + event.initDragEvent(aEventType, true, true, getContentWindow(), 0, 0, 0, 0, 0, 1.551 + false, false, false, false, 0, null, dataTransfer); 1.552 + 1.553 + return event; 1.554 +} 1.555 + 1.556 +/** 1.557 + * Resumes testing when all pages have been updated. 1.558 + * @param aCallback Called when done. If not specified, TestRunner.next is used. 1.559 + * @param aOnlyIfHidden If true, this resumes testing only when an update that 1.560 + * applies to pre-loaded, hidden pages is observed. If 1.561 + * false, this resumes testing when any update is observed. 1.562 + */ 1.563 +function whenPagesUpdated(aCallback, aOnlyIfHidden=false) { 1.564 + let page = { 1.565 + update: function (onlyIfHidden=false) { 1.566 + if (onlyIfHidden == aOnlyIfHidden) { 1.567 + NewTabUtils.allPages.unregister(this); 1.568 + executeSoon(aCallback || TestRunner.next); 1.569 + } 1.570 + } 1.571 + }; 1.572 + 1.573 + NewTabUtils.allPages.register(page); 1.574 + registerCleanupFunction(function () { 1.575 + NewTabUtils.allPages.unregister(page); 1.576 + }); 1.577 +} 1.578 + 1.579 +/** 1.580 + * Waits a small amount of time for search events to stop occurring in the 1.581 + * newtab page. 1.582 + * 1.583 + * newtab pages receive some search events around load time that are difficult 1.584 + * to predict. There are two categories of such events: (1) "State" events 1.585 + * triggered by engine notifications like engine-changed, due to the search 1.586 + * service initializing itself on app startup. This can happen when a test is 1.587 + * the first test to run. (2) "State" events triggered by the newtab page 1.588 + * itself when gSearch first sets itself up. newtab preloading makes these a 1.589 + * pain to predict. 1.590 + */ 1.591 +function whenSearchInitDone() { 1.592 + info("Waiting for initial search events..."); 1.593 + let numTicks = 0; 1.594 + function reset(event) { 1.595 + info("Got initial search event " + event.detail.type + 1.596 + ", waiting for more..."); 1.597 + numTicks = 0; 1.598 + } 1.599 + let eventName = "ContentSearchService"; 1.600 + getContentWindow().addEventListener(eventName, reset); 1.601 + let interval = window.setInterval(() => { 1.602 + if (++numTicks >= 100) { 1.603 + info("Done waiting for initial search events"); 1.604 + window.clearInterval(interval); 1.605 + getContentWindow().removeEventListener(eventName, reset); 1.606 + TestRunner.next(); 1.607 + } 1.608 + }, 0); 1.609 +}