mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
615 lines
19 KiB
JavaScript
615 lines
19 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
|
|
const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directorySource";
|
|
|
|
Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
|
|
// start with no directory links by default
|
|
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}");
|
|
|
|
let tmp = {};
|
|
Cu.import("resource://gre/modules/Promise.jsm", tmp);
|
|
Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp);
|
|
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
|
.getService(Ci.mozIJSSubScriptLoader)
|
|
.loadSubScript("chrome://browser/content/sanitize.js", tmp);
|
|
Cu.import("resource://gre/modules/Timer.jsm", tmp);
|
|
let {Promise, NewTabUtils, Sanitizer, clearTimeout} = tmp;
|
|
|
|
let uri = Services.io.newURI("about:newtab", null, null);
|
|
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
|
|
|
let isMac = ("nsILocalFileMac" in Ci);
|
|
let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc);
|
|
let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
|
|
let gWindow = window;
|
|
|
|
// The tests assume all three rows of sites are shown, but the window may be too
|
|
// short to actually show three rows. Resize it if necessary.
|
|
let requiredInnerHeight =
|
|
40 + 32 + // undo container + bottom margin
|
|
44 + 32 + // search bar + bottom margin
|
|
(3 * (150 + 32)) + // 3 rows * (tile height + title and bottom margin)
|
|
100; // breathing room
|
|
|
|
let oldInnerHeight = null;
|
|
if (gBrowser.contentWindow.innerHeight < requiredInnerHeight) {
|
|
oldInnerHeight = gBrowser.contentWindow.innerHeight;
|
|
info("Changing browser inner height from " + oldInnerHeight + " to " +
|
|
requiredInnerHeight);
|
|
gBrowser.contentWindow.innerHeight = requiredInnerHeight;
|
|
let screenHeight = {};
|
|
Cc["@mozilla.org/gfx/screenmanager;1"].
|
|
getService(Ci.nsIScreenManager).
|
|
primaryScreen.
|
|
GetAvailRectDisplayPix({}, {}, {}, screenHeight);
|
|
screenHeight = screenHeight.value;
|
|
if (screenHeight < gBrowser.contentWindow.outerHeight) {
|
|
info("Warning: Browser outer height is now " +
|
|
gBrowser.contentWindow.outerHeight + ", which is larger than the " +
|
|
"available screen height, " + screenHeight +
|
|
". That may cause problems.");
|
|
}
|
|
}
|
|
|
|
registerCleanupFunction(function () {
|
|
while (gWindow.gBrowser.tabs.length > 1)
|
|
gWindow.gBrowser.removeTab(gWindow.gBrowser.tabs[1]);
|
|
|
|
if (oldInnerHeight)
|
|
gBrowser.contentWindow.innerHeight = oldInnerHeight;
|
|
|
|
Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
|
|
Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE);
|
|
|
|
// Stop any update timers to prevent unexpected updates in later tests
|
|
let timer = NewTabUtils.allPages._scheduleUpdateTimeout;
|
|
if (timer) {
|
|
clearTimeout(timer);
|
|
delete NewTabUtils.allPages._scheduleUpdateTimeout;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Provide the default test function to start our test runner.
|
|
*/
|
|
function test() {
|
|
TestRunner.run();
|
|
}
|
|
|
|
/**
|
|
* The test runner that controls the execution flow of our tests.
|
|
*/
|
|
let TestRunner = {
|
|
/**
|
|
* Starts the test runner.
|
|
*/
|
|
run: function () {
|
|
waitForExplicitFinish();
|
|
|
|
this._iter = runTests();
|
|
this.next();
|
|
},
|
|
|
|
/**
|
|
* Runs the next available test or finishes if there's no test left.
|
|
*/
|
|
next: function () {
|
|
try {
|
|
TestRunner._iter.next();
|
|
} catch (e if e instanceof StopIteration) {
|
|
TestRunner.finish();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Finishes all tests and cleans up.
|
|
*/
|
|
finish: function () {
|
|
function cleanupAndFinish() {
|
|
clearHistory(function () {
|
|
whenPagesUpdated(finish);
|
|
NewTabUtils.restore();
|
|
});
|
|
}
|
|
|
|
let callbacks = NewTabUtils.links._populateCallbacks;
|
|
let numCallbacks = callbacks.length;
|
|
|
|
if (numCallbacks)
|
|
callbacks.splice(0, numCallbacks, cleanupAndFinish);
|
|
else
|
|
cleanupAndFinish();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the selected tab's content window.
|
|
* @return The content window.
|
|
*/
|
|
function getContentWindow() {
|
|
return gWindow.gBrowser.selectedBrowser.contentWindow;
|
|
}
|
|
|
|
/**
|
|
* Returns the selected tab's content document.
|
|
* @return The content document.
|
|
*/
|
|
function getContentDocument() {
|
|
return gWindow.gBrowser.selectedBrowser.contentDocument;
|
|
}
|
|
|
|
/**
|
|
* Returns the newtab grid of the selected tab.
|
|
* @return The newtab grid.
|
|
*/
|
|
function getGrid() {
|
|
return getContentWindow().gGrid;
|
|
}
|
|
|
|
/**
|
|
* Returns the cell at the given index of the selected tab's newtab grid.
|
|
* @param aIndex The cell index.
|
|
* @return The newtab cell.
|
|
*/
|
|
function getCell(aIndex) {
|
|
return getGrid().cells[aIndex];
|
|
}
|
|
|
|
/**
|
|
* Allows to provide a list of links that is used to construct the grid.
|
|
* @param aLinksPattern the pattern (see below)
|
|
*
|
|
* Example: setLinks("1,2,3")
|
|
* Result: [{url: "http://example.com/#1", title: "site#1"},
|
|
* {url: "http://example.com/#2", title: "site#2"}
|
|
* {url: "http://example.com/#3", title: "site#3"}]
|
|
*/
|
|
function setLinks(aLinks) {
|
|
let links = aLinks;
|
|
|
|
if (typeof links == "string") {
|
|
links = aLinks.split(/\s*,\s*/).map(function (id) {
|
|
return {url: "http://example.com/#" + id, title: "site#" + id};
|
|
});
|
|
}
|
|
|
|
// Call populateCache() once to make sure that all link fetching that is
|
|
// currently in progress has ended. We clear the history, fill it with the
|
|
// given entries and call populateCache() now again to make sure the cache
|
|
// has the desired contents.
|
|
NewTabUtils.links.populateCache(function () {
|
|
clearHistory(function () {
|
|
fillHistory(links, function () {
|
|
NewTabUtils.links.populateCache(function () {
|
|
NewTabUtils.allPages.update();
|
|
TestRunner.next();
|
|
}, true);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
function clearHistory(aCallback) {
|
|
Services.obs.addObserver(function observe(aSubject, aTopic, aData) {
|
|
Services.obs.removeObserver(observe, aTopic);
|
|
executeSoon(aCallback);
|
|
}, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
|
|
|
|
PlacesUtils.history.removeAllPages();
|
|
}
|
|
|
|
function fillHistory(aLinks, aCallback) {
|
|
let numLinks = aLinks.length;
|
|
if (!numLinks) {
|
|
if (aCallback)
|
|
executeSoon(aCallback);
|
|
return;
|
|
}
|
|
|
|
let transitionLink = Ci.nsINavHistoryService.TRANSITION_LINK;
|
|
|
|
// Important: To avoid test failures due to clock jitter on Windows XP, call
|
|
// Date.now() once here, not each time through the loop.
|
|
let now = Date.now() * 1000;
|
|
|
|
for (let i = 0; i < aLinks.length; i++) {
|
|
let link = aLinks[i];
|
|
let place = {
|
|
uri: makeURI(link.url),
|
|
title: link.title,
|
|
// Links are secondarily sorted by visit date descending, so decrease the
|
|
// visit date as we progress through the array so that links appear in the
|
|
// grid in the order they're present in the array.
|
|
visits: [{visitDate: now - i, transitionType: transitionLink}]
|
|
};
|
|
|
|
PlacesUtils.asyncHistory.updatePlaces(place, {
|
|
handleError: function () ok(false, "couldn't add visit to history"),
|
|
handleResult: function () {},
|
|
handleCompletion: function () {
|
|
if (--numLinks == 0 && aCallback)
|
|
aCallback();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allows to specify the list of pinned links (that have a fixed position in
|
|
* the grid.
|
|
* @param aLinksPattern the pattern (see below)
|
|
*
|
|
* Example: setPinnedLinks("3,,1")
|
|
* Result: 'http://example.com/#3' is pinned in the first cell. 'http://example.com/#1' is
|
|
* pinned in the third cell.
|
|
*/
|
|
function setPinnedLinks(aLinks) {
|
|
let links = aLinks;
|
|
|
|
if (typeof links == "string") {
|
|
links = aLinks.split(/\s*,\s*/).map(function (id) {
|
|
if (id)
|
|
return {url: "http://example.com/#" + id, title: "site#" + id};
|
|
});
|
|
}
|
|
|
|
let string = Cc["@mozilla.org/supports-string;1"]
|
|
.createInstance(Ci.nsISupportsString);
|
|
string.data = JSON.stringify(links);
|
|
Services.prefs.setComplexValue("browser.newtabpage.pinned",
|
|
Ci.nsISupportsString, string);
|
|
|
|
NewTabUtils.pinnedLinks.resetCache();
|
|
NewTabUtils.allPages.update();
|
|
}
|
|
|
|
/**
|
|
* Restore the grid state.
|
|
*/
|
|
function restore() {
|
|
whenPagesUpdated();
|
|
NewTabUtils.restore();
|
|
}
|
|
|
|
/**
|
|
* Creates a new tab containing 'about:newtab'.
|
|
*/
|
|
function addNewTabPageTab() {
|
|
let tab = gWindow.gBrowser.selectedTab = gWindow.gBrowser.addTab("about:newtab");
|
|
let browser = tab.linkedBrowser;
|
|
|
|
function whenNewTabLoaded() {
|
|
if (NewTabUtils.allPages.enabled) {
|
|
// Continue when the link cache has been populated.
|
|
NewTabUtils.links.populateCache(function () {
|
|
executeSoon(TestRunner.next);
|
|
});
|
|
} else {
|
|
// It's important that we call next() asynchronously.
|
|
// 'yield addNewTabPageTab()' would fail if next() is called
|
|
// synchronously because the iterator is already executing.
|
|
executeSoon(TestRunner.next);
|
|
}
|
|
}
|
|
|
|
// The new tab page might have been preloaded in the background.
|
|
if (browser.contentDocument.readyState == "complete") {
|
|
whenNewTabLoaded();
|
|
return;
|
|
}
|
|
|
|
// Wait for the new tab page to be loaded.
|
|
browser.addEventListener("load", function onLoad() {
|
|
browser.removeEventListener("load", onLoad, true);
|
|
whenNewTabLoaded();
|
|
}, true);
|
|
}
|
|
|
|
/**
|
|
* Compares the current grid arrangement with the given pattern.
|
|
* @param the pattern (see below)
|
|
* @param the array of sites to compare with (optional)
|
|
*
|
|
* Example: checkGrid("3p,2,,1p")
|
|
* Result: We expect the first cell to contain the pinned site 'http://example.com/#3'.
|
|
* The second cell contains 'http://example.com/#2'. The third cell is empty.
|
|
* The fourth cell contains the pinned site 'http://example.com/#4'.
|
|
*/
|
|
function checkGrid(aSitesPattern, aSites) {
|
|
let length = aSitesPattern.split(",").length;
|
|
let sites = (aSites || getGrid().sites).slice(0, length);
|
|
let current = sites.map(function (aSite) {
|
|
if (!aSite)
|
|
return "";
|
|
|
|
let pinned = aSite.isPinned();
|
|
let pinButton = aSite.node.querySelector(".newtab-control-pin");
|
|
let hasPinnedAttr = pinButton.hasAttribute("pinned");
|
|
|
|
if (pinned != hasPinnedAttr)
|
|
ok(false, "invalid state (site.isPinned() != site[pinned])");
|
|
|
|
return aSite.url.replace(/^http:\/\/example\.com\/#(\d+)$/, "$1") + (pinned ? "p" : "");
|
|
});
|
|
|
|
is(current, aSitesPattern, "grid status = " + aSitesPattern);
|
|
}
|
|
|
|
/**
|
|
* Blocks a site from the grid.
|
|
* @param aIndex The cell index.
|
|
*/
|
|
function blockCell(aIndex) {
|
|
whenPagesUpdated();
|
|
getCell(aIndex).site.block();
|
|
}
|
|
|
|
/**
|
|
* Pins a site on a given position.
|
|
* @param aIndex The cell index.
|
|
* @param aPinIndex The index the defines where the site should be pinned.
|
|
*/
|
|
function pinCell(aIndex, aPinIndex) {
|
|
getCell(aIndex).site.pin(aPinIndex);
|
|
}
|
|
|
|
/**
|
|
* Unpins the given cell's site.
|
|
* @param aIndex The cell index.
|
|
*/
|
|
function unpinCell(aIndex) {
|
|
whenPagesUpdated();
|
|
getCell(aIndex).site.unpin();
|
|
}
|
|
|
|
/**
|
|
* Simulates a drag and drop operation.
|
|
* @param aSourceIndex The cell index containing the dragged site.
|
|
* @param aDestIndex The cell index of the drop target.
|
|
*/
|
|
function simulateDrop(aSourceIndex, aDestIndex) {
|
|
let src = getCell(aSourceIndex).site.node;
|
|
let dest = getCell(aDestIndex).node;
|
|
|
|
// Drop 'src' onto 'dest' and continue testing when all newtab
|
|
// pages have been updated (i.e. the drop operation is completed).
|
|
startAndCompleteDragOperation(src, dest, whenPagesUpdated);
|
|
}
|
|
|
|
/**
|
|
* Simulates a drag and drop operation. Instead of rearranging a site that is
|
|
* is already contained in the newtab grid, this is used to simulate dragging
|
|
* an external link onto the grid e.g. the text from the URL bar.
|
|
* @param aDestIndex The cell index of the drop target.
|
|
*/
|
|
function simulateExternalDrop(aDestIndex) {
|
|
let dest = getCell(aDestIndex).node;
|
|
|
|
// Create an iframe that contains the external link we'll drag.
|
|
createExternalDropIframe().then(iframe => {
|
|
let link = iframe.contentDocument.getElementById("link");
|
|
|
|
// Drop 'link' onto 'dest'.
|
|
startAndCompleteDragOperation(link, dest, () => {
|
|
// Wait until the drop operation is complete
|
|
// and all newtab pages have been updated.
|
|
whenPagesUpdated(() => {
|
|
// Clean up and remove the iframe.
|
|
iframe.remove();
|
|
// Continue testing.
|
|
TestRunner.next();
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Starts and complete a drag-and-drop operation.
|
|
* @param aSource The node that is being dragged.
|
|
* @param aDest The node we're dragging aSource onto.
|
|
* @param aCallback The function that is called when we're done.
|
|
*/
|
|
function startAndCompleteDragOperation(aSource, aDest, aCallback) {
|
|
// Start by pressing the left mouse button.
|
|
synthesizeNativeMouseLDown(aSource);
|
|
|
|
// Move the mouse in 5px steps until the drag operation starts.
|
|
let offset = 0;
|
|
let interval = setInterval(() => {
|
|
synthesizeNativeMouseDrag(aSource, offset += 5);
|
|
}, 10);
|
|
|
|
// When the drag operation has started we'll move
|
|
// the dragged element to its target position.
|
|
aSource.addEventListener("dragstart", function onDragStart() {
|
|
aSource.removeEventListener("dragstart", onDragStart);
|
|
clearInterval(interval);
|
|
|
|
// Place the cursor above the drag target.
|
|
synthesizeNativeMouseMove(aDest);
|
|
});
|
|
|
|
// As soon as the dragged element hovers the target, we'll drop it.
|
|
aDest.addEventListener("dragenter", function onDragEnter() {
|
|
aDest.removeEventListener("dragenter", onDragEnter);
|
|
|
|
// Finish the drop operation.
|
|
synthesizeNativeMouseLUp(aDest);
|
|
aCallback();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Helper function that creates a temporary iframe in the about:newtab
|
|
* document. This will contain a link we can drag to the test the dropping
|
|
* of links from external documents.
|
|
*/
|
|
function createExternalDropIframe() {
|
|
const url = "data:text/html;charset=utf-8," +
|
|
"<a id='link' href='http://example.com/%2399'>link</a>";
|
|
|
|
let deferred = Promise.defer();
|
|
let doc = getContentDocument();
|
|
let iframe = doc.createElement("iframe");
|
|
iframe.setAttribute("src", url);
|
|
iframe.style.width = "50px";
|
|
iframe.style.height = "50px";
|
|
|
|
let margin = doc.getElementById("newtab-margin-top");
|
|
margin.appendChild(iframe);
|
|
|
|
iframe.addEventListener("load", function onLoad() {
|
|
iframe.removeEventListener("load", onLoad);
|
|
executeSoon(() => deferred.resolve(iframe));
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
/**
|
|
* Fires a synthetic 'mousedown' event on the current about:newtab page.
|
|
* @param aElement The element used to determine the cursor position.
|
|
*/
|
|
function synthesizeNativeMouseLDown(aElement) {
|
|
if (isLinux) {
|
|
let win = aElement.ownerDocument.defaultView;
|
|
EventUtils.synthesizeMouseAtCenter(aElement, {type: "mousedown"}, win);
|
|
} else {
|
|
let msg = isWindows ? 2 : 1;
|
|
synthesizeNativeMouseEvent(aElement, msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fires a synthetic 'mouseup' event on the current about:newtab page.
|
|
* @param aElement The element used to determine the cursor position.
|
|
*/
|
|
function synthesizeNativeMouseLUp(aElement) {
|
|
let msg = isWindows ? 4 : (isMac ? 2 : 7);
|
|
synthesizeNativeMouseEvent(aElement, msg);
|
|
}
|
|
|
|
/**
|
|
* Fires a synthetic mouse drag event on the current about:newtab page.
|
|
* @param aElement The element used to determine the cursor position.
|
|
* @param aOffsetX The left offset that is added to the position.
|
|
*/
|
|
function synthesizeNativeMouseDrag(aElement, aOffsetX) {
|
|
let msg = isMac ? 6 : 1;
|
|
synthesizeNativeMouseEvent(aElement, msg, aOffsetX);
|
|
}
|
|
|
|
/**
|
|
* Fires a synthetic 'mousemove' event on the current about:newtab page.
|
|
* @param aElement The element used to determine the cursor position.
|
|
*/
|
|
function synthesizeNativeMouseMove(aElement) {
|
|
let msg = isMac ? 5 : 1;
|
|
synthesizeNativeMouseEvent(aElement, msg);
|
|
}
|
|
|
|
/**
|
|
* Fires a synthetic mouse event on the current about:newtab page.
|
|
* @param aElement The element used to determine the cursor position.
|
|
* @param aOffsetX The left offset that is added to the position (optional).
|
|
* @param aOffsetY The top offset that is added to the position (optional).
|
|
*/
|
|
function synthesizeNativeMouseEvent(aElement, aMsg, aOffsetX = 0, aOffsetY = 0) {
|
|
let rect = aElement.getBoundingClientRect();
|
|
let win = aElement.ownerDocument.defaultView;
|
|
let x = aOffsetX + win.mozInnerScreenX + rect.left + rect.width / 2;
|
|
let y = aOffsetY + win.mozInnerScreenY + rect.top + rect.height / 2;
|
|
|
|
let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils);
|
|
|
|
let scale = utils.screenPixelsPerCSSPixel;
|
|
utils.sendNativeMouseEvent(x * scale, y * scale, aMsg, 0, null);
|
|
}
|
|
|
|
/**
|
|
* Sends a custom drag event to a given DOM element.
|
|
* @param aEventType The drag event's type.
|
|
* @param aTarget The DOM element that the event is dispatched to.
|
|
* @param aData The event's drag data (optional).
|
|
*/
|
|
function sendDragEvent(aEventType, aTarget, aData) {
|
|
let event = createDragEvent(aEventType, aData);
|
|
let ifaceReq = getContentWindow().QueryInterface(Ci.nsIInterfaceRequestor);
|
|
let windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
|
|
windowUtils.dispatchDOMEventViaPresShell(aTarget, event, true);
|
|
}
|
|
|
|
/**
|
|
* Creates a custom drag event.
|
|
* @param aEventType The drag event's type.
|
|
* @param aData The event's drag data (optional).
|
|
* @return The drag event.
|
|
*/
|
|
function createDragEvent(aEventType, aData) {
|
|
let dataTransfer = new (getContentWindow()).DataTransfer("dragstart", false);
|
|
dataTransfer.mozSetDataAt("text/x-moz-url", aData, 0);
|
|
let event = getContentDocument().createEvent("DragEvents");
|
|
event.initDragEvent(aEventType, true, true, getContentWindow(), 0, 0, 0, 0, 0,
|
|
false, false, false, false, 0, null, dataTransfer);
|
|
|
|
return event;
|
|
}
|
|
|
|
/**
|
|
* Resumes testing when all pages have been updated.
|
|
* @param aCallback Called when done. If not specified, TestRunner.next is used.
|
|
* @param aOnlyIfHidden If true, this resumes testing only when an update that
|
|
* applies to pre-loaded, hidden pages is observed. If
|
|
* false, this resumes testing when any update is observed.
|
|
*/
|
|
function whenPagesUpdated(aCallback, aOnlyIfHidden=false) {
|
|
let page = {
|
|
update: function (onlyIfHidden=false) {
|
|
if (onlyIfHidden == aOnlyIfHidden) {
|
|
NewTabUtils.allPages.unregister(this);
|
|
executeSoon(aCallback || TestRunner.next);
|
|
}
|
|
}
|
|
};
|
|
|
|
NewTabUtils.allPages.register(page);
|
|
registerCleanupFunction(function () {
|
|
NewTabUtils.allPages.unregister(page);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Waits a small amount of time for search events to stop occurring in the
|
|
* newtab page.
|
|
*
|
|
* newtab pages receive some search events around load time that are difficult
|
|
* to predict. There are two categories of such events: (1) "State" events
|
|
* triggered by engine notifications like engine-changed, due to the search
|
|
* service initializing itself on app startup. This can happen when a test is
|
|
* the first test to run. (2) "State" events triggered by the newtab page
|
|
* itself when gSearch first sets itself up. newtab preloading makes these a
|
|
* pain to predict.
|
|
*/
|
|
function whenSearchInitDone() {
|
|
info("Waiting for initial search events...");
|
|
let numTicks = 0;
|
|
function reset(event) {
|
|
info("Got initial search event " + event.detail.type +
|
|
", waiting for more...");
|
|
numTicks = 0;
|
|
}
|
|
let eventName = "ContentSearchService";
|
|
getContentWindow().addEventListener(eventName, reset);
|
|
let interval = window.setInterval(() => {
|
|
if (++numTicks >= 100) {
|
|
info("Done waiting for initial search events");
|
|
window.clearInterval(interval);
|
|
getContentWindow().removeEventListener(eventName, reset);
|
|
TestRunner.next();
|
|
}
|
|
}, 0);
|
|
}
|