toolkit/components/thumbnails/test/head.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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 let tmp = {};
michael@0 5 Cu.import("resource://gre/modules/PageThumbs.jsm", tmp);
michael@0 6 Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", tmp);
michael@0 7 Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp);
michael@0 8 Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
michael@0 9 Cu.import("resource://gre/modules/FileUtils.jsm", tmp);
michael@0 10 Cu.import("resource://gre/modules/osfile.jsm", tmp);
michael@0 11 let {PageThumbs, BackgroundPageThumbs, NewTabUtils, PageThumbsStorage, SessionStore, FileUtils, OS} = tmp;
michael@0 12
michael@0 13 Cu.import("resource://gre/modules/PlacesUtils.jsm");
michael@0 14
michael@0 15 let oldEnabledPref = Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled");
michael@0 16 Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", false);
michael@0 17
michael@0 18 registerCleanupFunction(function () {
michael@0 19 while (gBrowser.tabs.length > 1)
michael@0 20 gBrowser.removeTab(gBrowser.tabs[1]);
michael@0 21 Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", oldEnabledPref)
michael@0 22 });
michael@0 23
michael@0 24 /**
michael@0 25 * Provide the default test function to start our test runner.
michael@0 26 */
michael@0 27 function test() {
michael@0 28 TestRunner.run();
michael@0 29 }
michael@0 30
michael@0 31 /**
michael@0 32 * The test runner that controls the execution flow of our tests.
michael@0 33 */
michael@0 34 let TestRunner = {
michael@0 35 /**
michael@0 36 * Starts the test runner.
michael@0 37 */
michael@0 38 run: function () {
michael@0 39 waitForExplicitFinish();
michael@0 40
michael@0 41 SessionStore.promiseInitialized.then(function () {
michael@0 42 this._iter = runTests();
michael@0 43 if (this._iter) {
michael@0 44 this.next();
michael@0 45 } else {
michael@0 46 finish();
michael@0 47 }
michael@0 48 }.bind(this));
michael@0 49 },
michael@0 50
michael@0 51 /**
michael@0 52 * Runs the next available test or finishes if there's no test left.
michael@0 53 * @param aValue This value will be passed to the yielder via the runner's
michael@0 54 * iterator.
michael@0 55 */
michael@0 56 next: function (aValue) {
michael@0 57 try {
michael@0 58 let value = TestRunner._iter.send(aValue);
michael@0 59 if (value && typeof value.then == "function") {
michael@0 60 value.then(result => {
michael@0 61 next(result);
michael@0 62 }, error => {
michael@0 63 ok(false, error + "\n" + error.stack);
michael@0 64 });
michael@0 65 }
michael@0 66 } catch (e if e instanceof StopIteration) {
michael@0 67 finish();
michael@0 68 }
michael@0 69 }
michael@0 70 };
michael@0 71
michael@0 72 /**
michael@0 73 * Continues the current test execution.
michael@0 74 * @param aValue This value will be passed to the yielder via the runner's
michael@0 75 * iterator.
michael@0 76 */
michael@0 77 function next(aValue) {
michael@0 78 TestRunner.next(aValue);
michael@0 79 }
michael@0 80
michael@0 81 /**
michael@0 82 * Creates a new tab with the given URI.
michael@0 83 * @param aURI The URI that's loaded in the tab.
michael@0 84 * @param aCallback The function to call when the tab has loaded.
michael@0 85 */
michael@0 86 function addTab(aURI, aCallback) {
michael@0 87 let tab = gBrowser.selectedTab = gBrowser.addTab(aURI);
michael@0 88 whenLoaded(tab.linkedBrowser, aCallback);
michael@0 89 }
michael@0 90
michael@0 91 /**
michael@0 92 * Loads a new URI into the currently selected tab.
michael@0 93 * @param aURI The URI to load.
michael@0 94 */
michael@0 95 function navigateTo(aURI) {
michael@0 96 let browser = gBrowser.selectedTab.linkedBrowser;
michael@0 97 whenLoaded(browser);
michael@0 98 browser.loadURI(aURI);
michael@0 99 }
michael@0 100
michael@0 101 /**
michael@0 102 * Continues the current test execution when a load event for the given element
michael@0 103 * has been received.
michael@0 104 * @param aElement The DOM element to listen on.
michael@0 105 * @param aCallback The function to call when the load event was dispatched.
michael@0 106 */
michael@0 107 function whenLoaded(aElement, aCallback = next) {
michael@0 108 aElement.addEventListener("load", function onLoad() {
michael@0 109 aElement.removeEventListener("load", onLoad, true);
michael@0 110 executeSoon(aCallback);
michael@0 111 }, true);
michael@0 112 }
michael@0 113
michael@0 114 /**
michael@0 115 * Captures a screenshot for the currently selected tab, stores it in the cache,
michael@0 116 * retrieves it from the cache and compares pixel color values.
michael@0 117 * @param aRed The red component's intensity.
michael@0 118 * @param aGreen The green component's intensity.
michael@0 119 * @param aBlue The blue component's intensity.
michael@0 120 * @param aMessage The info message to print when comparing the pixel color.
michael@0 121 */
michael@0 122 function captureAndCheckColor(aRed, aGreen, aBlue, aMessage) {
michael@0 123 let browser = gBrowser.selectedBrowser;
michael@0 124 // We'll get oranges if the expiration filter removes the file during the
michael@0 125 // test.
michael@0 126 dontExpireThumbnailURLs([browser.currentURI.spec]);
michael@0 127
michael@0 128 // Capture the screenshot.
michael@0 129 PageThumbs.captureAndStore(browser, function () {
michael@0 130 retrieveImageDataForURL(browser.currentURI.spec, function ([r, g, b]) {
michael@0 131 is("" + [r,g,b], "" + [aRed, aGreen, aBlue], aMessage);
michael@0 132 next();
michael@0 133 });
michael@0 134 });
michael@0 135 }
michael@0 136
michael@0 137 /**
michael@0 138 * For a given URL, loads the corresponding thumbnail
michael@0 139 * to a canvas and passes its image data to the callback.
michael@0 140 * @param aURL The url associated with the thumbnail.
michael@0 141 * @param aCallback The function to pass the image data to.
michael@0 142 */
michael@0 143 function retrieveImageDataForURL(aURL, aCallback) {
michael@0 144 let width = 100, height = 100;
michael@0 145 let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
michael@0 146 // create a tab with a chrome:// URL so it can host the thumbnail image.
michael@0 147 // Note that we tried creating the element directly in the top-level chrome
michael@0 148 // document, but this caused a strange problem:
michael@0 149 // * call this with the url of an image.
michael@0 150 // * immediately change the image content.
michael@0 151 // * call this again with the same url (now holding different content)
michael@0 152 // The original image data would be used. Maybe the img hadn't been
michael@0 153 // collected yet and the platform noticed the same URL, so reused the
michael@0 154 // content? Not sure - but this solves the problem.
michael@0 155 addTab("chrome://global/content/mozilla.xhtml", () => {
michael@0 156 let doc = gBrowser.selectedBrowser.contentDocument;
michael@0 157 let htmlns = "http://www.w3.org/1999/xhtml";
michael@0 158 let img = doc.createElementNS(htmlns, "img");
michael@0 159 img.setAttribute("src", thumb);
michael@0 160
michael@0 161 whenLoaded(img, function () {
michael@0 162 let canvas = document.createElementNS(htmlns, "canvas");
michael@0 163 canvas.setAttribute("width", width);
michael@0 164 canvas.setAttribute("height", height);
michael@0 165
michael@0 166 // Draw the image to a canvas and compare the pixel color values.
michael@0 167 let ctx = canvas.getContext("2d");
michael@0 168 ctx.drawImage(img, 0, 0, width, height);
michael@0 169 let result = ctx.getImageData(0, 0, 100, 100).data;
michael@0 170 gBrowser.removeTab(gBrowser.selectedTab);
michael@0 171 aCallback(result);
michael@0 172 });
michael@0 173 });
michael@0 174 }
michael@0 175
michael@0 176 /**
michael@0 177 * Returns the file of the thumbnail with the given URL.
michael@0 178 * @param aURL The URL of the thumbnail.
michael@0 179 */
michael@0 180 function thumbnailFile(aURL) {
michael@0 181 return new FileUtils.File(PageThumbsStorage.getFilePathForURL(aURL));
michael@0 182 }
michael@0 183
michael@0 184 /**
michael@0 185 * Checks if a thumbnail for the given URL exists.
michael@0 186 * @param aURL The url associated to the thumbnail.
michael@0 187 */
michael@0 188 function thumbnailExists(aURL) {
michael@0 189 let file = thumbnailFile(aURL);
michael@0 190 return file.exists() && file.fileSize;
michael@0 191 }
michael@0 192
michael@0 193 /**
michael@0 194 * Removes the thumbnail for the given URL.
michael@0 195 * @param aURL The URL associated with the thumbnail.
michael@0 196 */
michael@0 197 function removeThumbnail(aURL) {
michael@0 198 let file = thumbnailFile(aURL);
michael@0 199 file.remove(false);
michael@0 200 }
michael@0 201
michael@0 202 /**
michael@0 203 * Asynchronously adds visits to a page, invoking a callback function when done.
michael@0 204 *
michael@0 205 * @param aPlaceInfo
michael@0 206 * One of the following: a string spec, an nsIURI, an object describing
michael@0 207 * the Place as described below, or an array of any such types. An
michael@0 208 * object describing a Place must look like this:
michael@0 209 * { uri: nsIURI of the page,
michael@0 210 * [optional] transition: one of the TRANSITION_* from
michael@0 211 * nsINavHistoryService,
michael@0 212 * [optional] title: title of the page,
michael@0 213 * [optional] visitDate: visit date in microseconds from the epoch
michael@0 214 * [optional] referrer: nsIURI of the referrer for this visit
michael@0 215 * }
michael@0 216 * @param [optional] aCallback
michael@0 217 * Function to be invoked on completion.
michael@0 218 */
michael@0 219 function addVisits(aPlaceInfo, aCallback) {
michael@0 220 let places = [];
michael@0 221 if (aPlaceInfo instanceof Ci.nsIURI) {
michael@0 222 places.push({ uri: aPlaceInfo });
michael@0 223 }
michael@0 224 else if (Array.isArray(aPlaceInfo)) {
michael@0 225 places = places.concat(aPlaceInfo);
michael@0 226 } else {
michael@0 227 places.push(aPlaceInfo)
michael@0 228 }
michael@0 229
michael@0 230 // Create mozIVisitInfo for each entry.
michael@0 231 let now = Date.now();
michael@0 232 for (let i = 0; i < places.length; i++) {
michael@0 233 if (typeof(places[i] == "string")) {
michael@0 234 places[i] = { uri: Services.io.newURI(places[i], "", null) };
michael@0 235 }
michael@0 236 if (!places[i].title) {
michael@0 237 places[i].title = "test visit for " + places[i].uri.spec;
michael@0 238 }
michael@0 239 places[i].visits = [{
michael@0 240 transitionType: places[i].transition === undefined ? PlacesUtils.history.TRANSITION_LINK
michael@0 241 : places[i].transition,
michael@0 242 visitDate: places[i].visitDate || (now++) * 1000,
michael@0 243 referrerURI: places[i].referrer
michael@0 244 }];
michael@0 245 }
michael@0 246
michael@0 247 PlacesUtils.asyncHistory.updatePlaces(
michael@0 248 places,
michael@0 249 {
michael@0 250 handleError: function AAV_handleError() {
michael@0 251 throw("Unexpected error in adding visit.");
michael@0 252 },
michael@0 253 handleResult: function () {},
michael@0 254 handleCompletion: function UP_handleCompletion() {
michael@0 255 if (aCallback)
michael@0 256 aCallback();
michael@0 257 }
michael@0 258 }
michael@0 259 );
michael@0 260 }
michael@0 261
michael@0 262 /**
michael@0 263 * Calls addVisits, and then forces the newtab module to repopulate its links.
michael@0 264 * See addVisits for parameter descriptions.
michael@0 265 */
michael@0 266 function addVisitsAndRepopulateNewTabLinks(aPlaceInfo, aCallback) {
michael@0 267 addVisits(aPlaceInfo, () => NewTabUtils.links.populateCache(aCallback, true));
michael@0 268 }
michael@0 269
michael@0 270 /**
michael@0 271 * Calls a given callback when the thumbnail for a given URL has been found
michael@0 272 * on disk. Keeps trying until the thumbnail has been created.
michael@0 273 *
michael@0 274 * @param aURL The URL of the thumbnail's page.
michael@0 275 * @param [optional] aCallback
michael@0 276 * Function to be invoked on completion.
michael@0 277 */
michael@0 278 function whenFileExists(aURL, aCallback = next) {
michael@0 279 let callback = aCallback;
michael@0 280 if (!thumbnailExists(aURL)) {
michael@0 281 callback = function () whenFileExists(aURL, aCallback);
michael@0 282 }
michael@0 283
michael@0 284 executeSoon(callback);
michael@0 285 }
michael@0 286
michael@0 287 /**
michael@0 288 * Calls a given callback when the given file has been removed.
michael@0 289 * Keeps trying until the file is removed.
michael@0 290 *
michael@0 291 * @param aFile The file that is being removed
michael@0 292 * @param [optional] aCallback
michael@0 293 * Function to be invoked on completion.
michael@0 294 */
michael@0 295 function whenFileRemoved(aFile, aCallback) {
michael@0 296 let callback = aCallback;
michael@0 297 if (aFile.exists()) {
michael@0 298 callback = function () whenFileRemoved(aFile, aCallback);
michael@0 299 }
michael@0 300
michael@0 301 executeSoon(callback || next);
michael@0 302 }
michael@0 303
michael@0 304 function wait(aMillis) {
michael@0 305 setTimeout(next, aMillis);
michael@0 306 }
michael@0 307
michael@0 308 /**
michael@0 309 * Makes sure that a given list of URLs is not implicitly expired.
michael@0 310 *
michael@0 311 * @param aURLs The list of URLs that should not be expired.
michael@0 312 */
michael@0 313 function dontExpireThumbnailURLs(aURLs) {
michael@0 314 let dontExpireURLs = (cb) => cb(aURLs);
michael@0 315 PageThumbs.addExpirationFilter(dontExpireURLs);
michael@0 316
michael@0 317 registerCleanupFunction(function () {
michael@0 318 PageThumbs.removeExpirationFilter(dontExpireURLs);
michael@0 319 });
michael@0 320 }
michael@0 321
michael@0 322 function bgCapture(aURL, aOptions) {
michael@0 323 bgCaptureWithMethod("capture", aURL, aOptions);
michael@0 324 }
michael@0 325
michael@0 326 function bgCaptureIfMissing(aURL, aOptions) {
michael@0 327 bgCaptureWithMethod("captureIfMissing", aURL, aOptions);
michael@0 328 }
michael@0 329
michael@0 330 function bgCaptureWithMethod(aMethodName, aURL, aOptions = {}) {
michael@0 331 // We'll get oranges if the expiration filter removes the file during the
michael@0 332 // test.
michael@0 333 dontExpireThumbnailURLs([aURL]);
michael@0 334 if (!aOptions.onDone)
michael@0 335 aOptions.onDone = next;
michael@0 336 BackgroundPageThumbs[aMethodName](aURL, aOptions);
michael@0 337 }
michael@0 338
michael@0 339 function bgTestPageURL(aOpts = {}) {
michael@0 340 let TEST_PAGE_URL = "http://mochi.test:8888/browser/toolkit/components/thumbnails/test/thumbnails_background.sjs";
michael@0 341 return TEST_PAGE_URL + "?" + encodeURIComponent(JSON.stringify(aOpts));
michael@0 342 }
michael@0 343
michael@0 344 function bgAddCrashObserver() {
michael@0 345 let crashed = false;
michael@0 346 Services.obs.addObserver(function crashObserver(subject, topic, data) {
michael@0 347 is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
michael@0 348 ok(subject instanceof Components.interfaces.nsIPropertyBag2,
michael@0 349 'Subject implements nsIPropertyBag2.');
michael@0 350 // we might see this called as the process terminates due to previous tests.
michael@0 351 // We are only looking for "abnormal" exits...
michael@0 352 if (!subject.hasKey("abnormal")) {
michael@0 353 info("This is a normal termination and isn't the one we are looking for...");
michael@0 354 return;
michael@0 355 }
michael@0 356 Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
michael@0 357 crashed = true;
michael@0 358
michael@0 359 var dumpID;
michael@0 360 if ('nsICrashReporter' in Components.interfaces) {
michael@0 361 dumpID = subject.getPropertyAsAString('dumpID');
michael@0 362 ok(dumpID, "dumpID is present and not an empty string");
michael@0 363 }
michael@0 364
michael@0 365 if (dumpID) {
michael@0 366 var minidumpDirectory = getMinidumpDirectory();
michael@0 367 removeFile(minidumpDirectory, dumpID + '.dmp');
michael@0 368 removeFile(minidumpDirectory, dumpID + '.extra');
michael@0 369 }
michael@0 370 }, 'ipc:content-shutdown', false);
michael@0 371 return {
michael@0 372 get crashed() crashed
michael@0 373 };
michael@0 374 }
michael@0 375
michael@0 376 function bgInjectCrashContentScript() {
michael@0 377 const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js";
michael@0 378 let thumbnailBrowser = BackgroundPageThumbs._thumbBrowser;
michael@0 379 let mm = thumbnailBrowser.messageManager;
michael@0 380 mm.loadFrameScript(TEST_CONTENT_HELPER, false);
michael@0 381 return mm;
michael@0 382 }
michael@0 383
michael@0 384 function getMinidumpDirectory() {
michael@0 385 var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
michael@0 386 dir.append("minidumps");
michael@0 387 return dir;
michael@0 388 }
michael@0 389
michael@0 390 function removeFile(directory, filename) {
michael@0 391 var file = directory.clone();
michael@0 392 file.append(filename);
michael@0 393 if (file.exists()) {
michael@0 394 file.remove(false);
michael@0 395 }
michael@0 396 }

mercurial