docshell/test/chrome/docshell_helpers.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
-rwxr-xr-x

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

michael@0 1 /**
michael@0 2 * Import common SimpleTest methods so that they're usable in this window.
michael@0 3 */
michael@0 4 var imports = [ "SimpleTest", "is", "isnot", "ise", "ok", "onerror", "todo",
michael@0 5 "todo_is", "todo_isnot" ];
michael@0 6 for each (var name in imports) {
michael@0 7 window[name] = window.opener.wrappedJSObject[name];
michael@0 8 }
michael@0 9
michael@0 10 /**
michael@0 11 * Define global constants and variables.
michael@0 12 */
michael@0 13 const NAV_NONE = 0;
michael@0 14 const NAV_BACK = 1;
michael@0 15 const NAV_FORWARD = 2;
michael@0 16 const NAV_URI = 3;
michael@0 17 const NAV_RELOAD = 4;
michael@0 18
michael@0 19 var gExpectedEvents; // an array of events which are expected to
michael@0 20 // be triggered by this navigation
michael@0 21 var gUnexpectedEvents; // an array of event names which are NOT expected
michael@0 22 // to be triggered by this navigation
michael@0 23 var gFinalEvent; // true if the last expected event has fired
michael@0 24 var gUrisNotInBFCache = []; // an array of uri's which shouldn't be stored
michael@0 25 // in the bfcache
michael@0 26 var gNavType = NAV_NONE; // defines the most recent navigation type
michael@0 27 // executed by doPageNavigation
michael@0 28 var gOrigMaxTotalViewers = // original value of max_total_viewers,
michael@0 29 undefined; // to be restored at end of test
michael@0 30
michael@0 31 var gExtractedPath = null; //used to cache file path for extracting files from a .jar file
michael@0 32
michael@0 33 /**
michael@0 34 * The doPageNavigation() function performs page navigations asynchronously,
michael@0 35 * listens for specified events, and compares actual events with a list of
michael@0 36 * expected events. When all expected events have occurred, an optional
michael@0 37 * callback can be notified. The parameter passed to this function is an
michael@0 38 * object with the following properties:
michael@0 39 *
michael@0 40 * uri: if !undefined, the browser will navigate to this uri
michael@0 41 *
michael@0 42 * back: if true, the browser will execute goBack()
michael@0 43 *
michael@0 44 * forward: if true, the browser will execute goForward()
michael@0 45 *
michael@0 46 * reload: if true, the browser will execute reload()
michael@0 47 *
michael@0 48 * eventsToListenFor: an array containing one or more of the following event
michael@0 49 * types to listen for: "pageshow", "pagehide", "onload",
michael@0 50 * "onunload". If this property is undefined, only a
michael@0 51 * single "pageshow" events will be listened for. If this
michael@0 52 * property is explicitly empty, [], then no events will
michael@0 53 * be listened for.
michael@0 54 *
michael@0 55 * expectedEvents: an array of one or more expectedEvent objects,
michael@0 56 * corresponding to the events which are expected to be
michael@0 57 * fired for this navigation. Each object has the
michael@0 58 * following properties:
michael@0 59 *
michael@0 60 * type: one of the event type strings
michael@0 61 * title (optional): the title of the window the
michael@0 62 * event belongs to
michael@0 63 * persisted (optional): the event's expected
michael@0 64 * .persisted attribute
michael@0 65 *
michael@0 66 * This function will verify that events with the
michael@0 67 * specified properties are fired in the same order as
michael@0 68 * specified in the array. If .title or .persisted
michael@0 69 * properties for an expectedEvent are undefined, those
michael@0 70 * properties will not be verified for that particular
michael@0 71 * event.
michael@0 72 *
michael@0 73 * This property is ignored if eventsToListenFor is
michael@0 74 * undefined or [].
michael@0 75 *
michael@0 76 * preventBFCache: if true, an unload handler will be added to the loaded
michael@0 77 * page to prevent it from being bfcached. This property
michael@0 78 * has no effect when eventsToListenFor is [].
michael@0 79 *
michael@0 80 * onNavComplete: a callback which is notified after all expected events
michael@0 81 * have occurred, or after a timeout has elapsed. This
michael@0 82 * callback is not notified if eventsToListenFor is [].
michael@0 83 *
michael@0 84 * There must be an expectedEvent object for each event of the types in
michael@0 85 * eventsToListenFor which is triggered by this navigation. For example, if
michael@0 86 * eventsToListenFor = [ "pagehide", "pageshow" ], then expectedEvents
michael@0 87 * must contain an object for each pagehide and pageshow event which occurs as
michael@0 88 * a result of this navigation.
michael@0 89 */
michael@0 90 function doPageNavigation(params) {
michael@0 91 // Parse the parameters.
michael@0 92 let back = params.back ? params.back : false;
michael@0 93 let forward = params.forward ? params.forward : false;
michael@0 94 let reload = params.reload ? params.reload : false;
michael@0 95 let uri = params.uri ? params.uri : false;
michael@0 96 let eventsToListenFor = typeof(params.eventsToListenFor) != "undefined" ?
michael@0 97 params.eventsToListenFor : ["pageshow"];
michael@0 98 gExpectedEvents = typeof(params.eventsToListenFor) == "undefined" ||
michael@0 99 eventsToListenFor.length == 0 ? undefined : params.expectedEvents;
michael@0 100 gUnexpectedEvents = typeof(params.eventsToListenFor) == "undefined" ||
michael@0 101 eventsToListenFor.length == 0 ? undefined : params.unexpectedEvents;
michael@0 102 let preventBFCache = (typeof[params.preventBFCache] == "undefined") ?
michael@0 103 false : params.preventBFCache;
michael@0 104 let waitOnly = (typeof(params.waitForEventsOnly) == "boolean"
michael@0 105 && params.waitForEventsOnly);
michael@0 106
michael@0 107 // Do some sanity checking on arguments.
michael@0 108 if (back && forward)
michael@0 109 throw "Can't specify both back and forward";
michael@0 110 if (back && uri)
michael@0 111 throw "Can't specify both back and a uri";
michael@0 112 if (forward && uri)
michael@0 113 throw "Can't specify both forward and a uri";
michael@0 114 if (reload && (forward || back || uri))
michael@0 115 throw "Can't specify reload and another navigation type";
michael@0 116 if (!back && !forward && !uri && !reload && !waitOnly)
michael@0 117 throw "Must specify back or foward or reload or uri";
michael@0 118 if (params.onNavComplete && eventsToListenFor.length == 0)
michael@0 119 throw "Can't use onNavComplete when eventsToListenFor == []";
michael@0 120 if (params.preventBFCache && eventsToListenFor.length == 0)
michael@0 121 throw "Can't use preventBFCache when eventsToListenFor == []";
michael@0 122 if (params.preventBFCache && waitOnly)
michael@0 123 throw "Can't prevent bfcaching when only waiting for events";
michael@0 124 if (waitOnly && typeof(params.onNavComplete) == "undefined")
michael@0 125 throw "Must specify onNavComplete when specifying waitForEventsOnly";
michael@0 126 if (waitOnly && (back || forward || reload || uri))
michael@0 127 throw "Can't specify a navigation type when using waitForEventsOnly";
michael@0 128 for each (let anEventType in eventsToListenFor) {
michael@0 129 let eventFound = false;
michael@0 130 if ( (anEventType == "pageshow") && (!gExpectedEvents) )
michael@0 131 eventFound = true;
michael@0 132 for each (let anExpectedEvent in gExpectedEvents) {
michael@0 133 if (anExpectedEvent.type == anEventType)
michael@0 134 eventFound = true;
michael@0 135 }
michael@0 136 for each (let anExpectedEventType in gUnexpectedEvents) {
michael@0 137 if (anExpectedEventType == anEventType)
michael@0 138 eventFound = true;
michael@0 139 }
michael@0 140 if (!eventFound)
michael@0 141 throw "Event type " + anEventType + " is specified in " +
michael@0 142 "eventsToListenFor, but not in expectedEvents";
michael@0 143 }
michael@0 144
michael@0 145 // If the test explicitly sets .eventsToListenFor to [], don't wait for any
michael@0 146 // events.
michael@0 147 gFinalEvent = eventsToListenFor.length == 0 ? true : false;
michael@0 148
michael@0 149 // Add an event listener for each type of event in the .eventsToListenFor
michael@0 150 // property of the input parameters.
michael@0 151 for each (let eventType in eventsToListenFor) {
michael@0 152 dump("TEST: registering a listener for " + eventType + " events\n");
michael@0 153 TestWindow.getBrowser().addEventListener(eventType, pageEventListener,
michael@0 154 true);
michael@0 155 }
michael@0 156
michael@0 157 // Perform the specified navigation.
michael@0 158 if (back) {
michael@0 159 gNavType = NAV_BACK;
michael@0 160 TestWindow.getBrowser().goBack();
michael@0 161 }
michael@0 162 else if (forward) {
michael@0 163 gNavType = NAV_FORWARD;
michael@0 164 TestWindow.getBrowser().goForward();
michael@0 165 }
michael@0 166 else if (uri) {
michael@0 167 gNavType = NAV_URI;
michael@0 168 TestWindow.getBrowser().loadURI(uri);
michael@0 169 }
michael@0 170 else if (reload) {
michael@0 171 gNavType = NAV_RELOAD;
michael@0 172 TestWindow.getBrowser().reload();
michael@0 173 }
michael@0 174 else if (waitOnly) {
michael@0 175 gNavType = NAV_NONE;
michael@0 176 }
michael@0 177 else {
michael@0 178 throw "No valid navigation type passed to doPageNavigation!";
michael@0 179 }
michael@0 180
michael@0 181 // If we're listening for events and there is an .onNavComplete callback,
michael@0 182 // wait for all events to occur, and then call doPageNavigation_complete().
michael@0 183 if (eventsToListenFor.length > 0 && params.onNavComplete)
michael@0 184 {
michael@0 185 waitForTrue(
michael@0 186 function() { return gFinalEvent; },
michael@0 187 function() {
michael@0 188 doPageNavigation_complete(eventsToListenFor, params.onNavComplete,
michael@0 189 preventBFCache);
michael@0 190 } );
michael@0 191 }
michael@0 192 }
michael@0 193
michael@0 194 /**
michael@0 195 * Finish doPageNavigation(), by removing event listeners, adding an unload
michael@0 196 * handler if appropriate, and calling the onNavComplete callback. This
michael@0 197 * function is called after all the expected events for this navigation have
michael@0 198 * occurred.
michael@0 199 */
michael@0 200 function doPageNavigation_complete(eventsToListenFor, onNavComplete,
michael@0 201 preventBFCache) {
michael@0 202 // Unregister our event listeners.
michael@0 203 dump("TEST: removing event listeners\n");
michael@0 204 for each (let eventType in eventsToListenFor) {
michael@0 205 TestWindow.getBrowser().removeEventListener(eventType, pageEventListener,
michael@0 206 true);
michael@0 207 }
michael@0 208
michael@0 209 // If the .preventBFCache property was set, add an empty unload handler to
michael@0 210 // prevent the page from being bfcached.
michael@0 211 let uri = TestWindow.getBrowser().currentURI.spec;
michael@0 212 if (preventBFCache) {
michael@0 213 TestWindow.getWindow().addEventListener("unload", function() {
michael@0 214 dump("TEST: Called dummy unload function to prevent page from " +
michael@0 215 "being bfcached.\n");
michael@0 216 }, true);
michael@0 217
michael@0 218 // Save the current uri in an array of uri's which shouldn't be
michael@0 219 // stored in the bfcache, for later verification.
michael@0 220 if (!(uri in gUrisNotInBFCache)) {
michael@0 221 gUrisNotInBFCache.push(uri);
michael@0 222 }
michael@0 223 } else if (gNavType == NAV_URI) {
michael@0 224 // If we're navigating to a uri and .preventBFCache was not
michael@0 225 // specified, splice it out of gUrisNotInBFCache if it's there.
michael@0 226 gUrisNotInBFCache.forEach(
michael@0 227 function(element, index, array) {
michael@0 228 if (element == uri) {
michael@0 229 array.splice(index, 1);
michael@0 230 }
michael@0 231 }, this);
michael@0 232 }
michael@0 233
michael@0 234 // Notify the callback now that we're done.
michael@0 235 onNavComplete.call();
michael@0 236 }
michael@0 237
michael@0 238 /**
michael@0 239 * Allows a test to wait for page navigation events, and notify a
michael@0 240 * callback when they've all been received. This works exactly the
michael@0 241 * same as doPageNavigation(), except that no navigation is initiated.
michael@0 242 */
michael@0 243 function waitForPageEvents(params) {
michael@0 244 params.waitForEventsOnly = true;
michael@0 245 doPageNavigation(params);
michael@0 246 }
michael@0 247
michael@0 248 /**
michael@0 249 * The event listener which listens for expectedEvents.
michael@0 250 */
michael@0 251 function pageEventListener(event) {
michael@0 252 try {
michael@0 253 dump("TEST: eventListener received a " + event.type + " event for page " +
michael@0 254 event.originalTarget.title + ", persisted=" + event.persisted + "\n");
michael@0 255 } catch(e) {
michael@0 256 // Ignore any exception.
michael@0 257 }
michael@0 258
michael@0 259 // If this page shouldn't be in the bfcache because it was previously
michael@0 260 // loaded with .preventBFCache, make sure that its pageshow event
michael@0 261 // has .persisted = false, even if the test doesn't explicitly test
michael@0 262 // for .persisted.
michael@0 263 if ( (event.type == "pageshow") &&
michael@0 264 (gNavType == NAV_BACK || gNavType == NAV_FORWARD) ) {
michael@0 265 let uri = TestWindow.getBrowser().currentURI.spec;
michael@0 266 if (uri in gUrisNotInBFCache) {
michael@0 267 ok(!event.persisted, "pageshow event has .persisted = false, even " +
michael@0 268 "though it was loaded with .preventBFCache previously\n");
michael@0 269 }
michael@0 270 }
michael@0 271
michael@0 272 if (typeof(gUnexpectedEvents) != "undefined") {
michael@0 273 is(gUnexpectedEvents.indexOf(event.type), -1,
michael@0 274 "Should not get unexpected event " + event.type);
michael@0 275 }
michael@0 276
michael@0 277 // If no expected events were specified, mark the final event as having been
michael@0 278 // triggered when a pageshow event is fired; this will allow
michael@0 279 // doPageNavigation() to return.
michael@0 280 if ((typeof(gExpectedEvents) == "undefined") && event.type == "pageshow")
michael@0 281 {
michael@0 282 setTimeout(function() { gFinalEvent = true; }, 0);
michael@0 283 return;
michael@0 284 }
michael@0 285
michael@0 286 // If there are explicitly no expected events, but we receive one, it's an
michael@0 287 // error.
michael@0 288 if (gExpectedEvents.length == 0) {
michael@0 289 ok(false, "Unexpected event (" + event.type + ") occurred");
michael@0 290 return;
michael@0 291 }
michael@0 292
michael@0 293 // Grab the next expected event, and compare its attributes against the
michael@0 294 // actual event.
michael@0 295 let expected = gExpectedEvents.shift();
michael@0 296
michael@0 297 is(event.type, expected.type,
michael@0 298 "A " + expected.type + " event was expected, but a " +
michael@0 299 event.type + " event occurred");
michael@0 300
michael@0 301 if (typeof(expected.title) != "undefined") {
michael@0 302 ok(event.originalTarget instanceof HTMLDocument,
michael@0 303 "originalTarget for last " + event.type +
michael@0 304 " event not an HTMLDocument");
michael@0 305 is(event.originalTarget.title, expected.title,
michael@0 306 "A " + event.type + " event was expected for page " +
michael@0 307 expected.title + ", but was fired for page " +
michael@0 308 event.originalTarget.title);
michael@0 309 }
michael@0 310
michael@0 311 if (typeof(expected.persisted) != "undefined") {
michael@0 312 is(event.persisted, expected.persisted,
michael@0 313 "The persisted property of the " + event.type + " event on page " +
michael@0 314 event.originalTarget.location + " had an unexpected value");
michael@0 315 }
michael@0 316
michael@0 317 if ("visibilityState" in expected) {
michael@0 318 is(event.originalTarget.visibilityState, expected.visibilityState,
michael@0 319 "The visibilityState property of the document on page " +
michael@0 320 event.originalTarget.location + " had an unexpected value");
michael@0 321 }
michael@0 322
michael@0 323 if ("hidden" in expected) {
michael@0 324 is(event.originalTarget.hidden, expected.hidden,
michael@0 325 "The hidden property of the document on page " +
michael@0 326 event.originalTarget.location + " had an unexpected value");
michael@0 327 }
michael@0 328
michael@0 329 // If we're out of expected events, let doPageNavigation() return.
michael@0 330 if (gExpectedEvents.length == 0)
michael@0 331 setTimeout(function() { gFinalEvent = true; }, 0);
michael@0 332 }
michael@0 333
michael@0 334 /**
michael@0 335 * End a test.
michael@0 336 */
michael@0 337 function finish() {
michael@0 338 // Work around bug 467960.
michael@0 339 var history = TestWindow.getBrowser().webNavigation.sessionHistory;
michael@0 340 history.PurgeHistory(history.count);
michael@0 341
michael@0 342 // If the test changed the value of max_total_viewers via a call to
michael@0 343 // enableBFCache(), then restore it now.
michael@0 344 if (typeof(gOrigMaxTotalViewers) != "undefined") {
michael@0 345 var prefs = Components.classes["@mozilla.org/preferences-service;1"]
michael@0 346 .getService(Components.interfaces.nsIPrefBranch);
michael@0 347 prefs.setIntPref("browser.sessionhistory.max_total_viewers",
michael@0 348 gOrigMaxTotalViewers);
michael@0 349 }
michael@0 350
michael@0 351 // Close the test window and signal the framework that the test is done.
michael@0 352 let opener = window.opener;
michael@0 353 let SimpleTest = opener.wrappedJSObject.SimpleTest;
michael@0 354
michael@0 355 // Wait for the window to be closed before finishing the test
michael@0 356 let ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
michael@0 357 .getService(Components.interfaces.nsIWindowWatcher);
michael@0 358 ww.registerNotification(function(subject, topic, data) {
michael@0 359 if (topic == "domwindowclosed") {
michael@0 360 ww.unregisterNotification(arguments.callee);
michael@0 361 SimpleTest.waitForFocus(function() {
michael@0 362 SimpleTest.finish();
michael@0 363 }, opener);
michael@0 364 }
michael@0 365 });
michael@0 366
michael@0 367 window.close();
michael@0 368 }
michael@0 369
michael@0 370 /**
michael@0 371 * Helper function which waits until another function returns true, or until a
michael@0 372 * timeout occurs, and then notifies a callback.
michael@0 373 *
michael@0 374 * Parameters:
michael@0 375 *
michael@0 376 * fn: a function which is evaluated repeatedly, and when it turns true,
michael@0 377 * the onWaitComplete callback is notified.
michael@0 378 *
michael@0 379 * onWaitComplete: a callback which will be notified when fn() returns
michael@0 380 * true, or when a timeout occurs.
michael@0 381 *
michael@0 382 * timeout: a timeout, in seconds or ms, after which waitForTrue() will
michael@0 383 * fail an assertion and then return, even if the fn function never
michael@0 384 * returns true. If timeout is undefined, waitForTrue() will never
michael@0 385 * time out.
michael@0 386 */
michael@0 387 function waitForTrue(fn, onWaitComplete, timeout) {
michael@0 388 var start = new Date().valueOf();
michael@0 389 if (typeof(timeout) != "undefined") {
michael@0 390 // If timeoutWait is less than 500, assume it represents seconds, and
michael@0 391 // convert to ms.
michael@0 392 if (timeout < 500)
michael@0 393 timeout *= 1000;
michael@0 394 }
michael@0 395
michael@0 396 // Loop until the test function returns true, or until a timeout occurs,
michael@0 397 // if a timeout is defined.
michael@0 398 var intervalid;
michael@0 399 intervalid =
michael@0 400 setInterval(
michael@0 401 function() {
michael@0 402 var timeoutHit = false;
michael@0 403 if (typeof(timeout) != "undefined") {
michael@0 404 timeoutHit = new Date().valueOf() - start >=
michael@0 405 timeout ? true : false;
michael@0 406 if (timeoutHit) {
michael@0 407 ok(false, "Timed out waiting for condition");
michael@0 408 }
michael@0 409 }
michael@0 410 if (timeoutHit || fn.call()) {
michael@0 411 // Stop calling the test function and notify the callback.
michael@0 412 clearInterval(intervalid);
michael@0 413 onWaitComplete.call();
michael@0 414 }
michael@0 415 }, 20);
michael@0 416 }
michael@0 417
michael@0 418 /**
michael@0 419 * Enable or disable the bfcache.
michael@0 420 *
michael@0 421 * Parameters:
michael@0 422 *
michael@0 423 * enable: if true, set max_total_viewers to -1 (the default); if false, set
michael@0 424 * to 0 (disabled), if a number, set it to that specific number
michael@0 425 */
michael@0 426 function enableBFCache(enable) {
michael@0 427 var prefs = Components.classes["@mozilla.org/preferences-service;1"]
michael@0 428 .getService(Components.interfaces.nsIPrefBranch);
michael@0 429
michael@0 430 // If this is the first time the test called enableBFCache(),
michael@0 431 // store the original value of max_total_viewers, so it can
michael@0 432 // be restored at the end of the test.
michael@0 433 if (typeof(gOrigMaxTotalViewers) == "undefined") {
michael@0 434 gOrigMaxTotalViewers =
michael@0 435 prefs.getIntPref("browser.sessionhistory.max_total_viewers");
michael@0 436 }
michael@0 437
michael@0 438 if (typeof(enable) == "boolean") {
michael@0 439 if (enable)
michael@0 440 prefs.setIntPref("browser.sessionhistory.max_total_viewers", -1);
michael@0 441 else
michael@0 442 prefs.setIntPref("browser.sessionhistory.max_total_viewers", 0);
michael@0 443 }
michael@0 444 else if (typeof(enable) == "number") {
michael@0 445 prefs.setIntPref("browser.sessionhistory.max_total_viewers", enable);
michael@0 446 }
michael@0 447 }
michael@0 448
michael@0 449 /*
michael@0 450 * get http root for local tests. Use a single extractJarToTmp instead of
michael@0 451 * extracting for each test.
michael@0 452 * Returns a file://path if we have a .jar file
michael@0 453 */
michael@0 454 function getHttpRoot() {
michael@0 455 var location = window.location.href;
michael@0 456 location = getRootDirectory(location);
michael@0 457 var jar = getJar(location);
michael@0 458 if (jar != null) {
michael@0 459 if (gExtractedPath == null) {
michael@0 460 var resolved = extractJarToTmp(jar);
michael@0 461 gExtractedPath = resolved.path;
michael@0 462 }
michael@0 463 } else {
michael@0 464 return null;
michael@0 465 }
michael@0 466 return "file://" + gExtractedPath + '/';
michael@0 467 }
michael@0 468
michael@0 469 /**
michael@0 470 * Returns the full HTTP url for a file in the mochitest docshell test
michael@0 471 * directory.
michael@0 472 */
michael@0 473 function getHttpUrl(filename) {
michael@0 474 var root = getHttpRoot();
michael@0 475 if (root == null) {
michael@0 476 root = "http://mochi.test:8888/chrome/docshell/test/chrome/";
michael@0 477 }
michael@0 478 return root + filename;
michael@0 479 }
michael@0 480
michael@0 481 /**
michael@0 482 * A convenience object with methods that return the current test window,
michael@0 483 * browser, and document.
michael@0 484 */
michael@0 485 var TestWindow = {};
michael@0 486 TestWindow.getWindow = function () {
michael@0 487 return document.getElementById("content").contentWindow;
michael@0 488 }
michael@0 489 TestWindow.getBrowser = function () {
michael@0 490 return document.getElementById("content");
michael@0 491 }
michael@0 492 TestWindow.getDocument = function () {
michael@0 493 return document.getElementById("content").contentDocument;
michael@0 494 }

mercurial