browser/base/content/test/newtab/head.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial