summaryrefslogtreecommitdiff
path: root/toolkit/components/thumbnails/test/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/thumbnails/test/head.js')
-rw-r--r--toolkit/components/thumbnails/test/head.js356
1 files changed, 356 insertions, 0 deletions
diff --git a/toolkit/components/thumbnails/test/head.js b/toolkit/components/thumbnails/test/head.js
new file mode 100644
index 0000000000..e8229508a6
--- /dev/null
+++ b/toolkit/components/thumbnails/test/head.js
@@ -0,0 +1,356 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var tmp = {};
+Cu.import("resource://gre/modules/PageThumbs.jsm", tmp);
+Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", tmp);
+Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp);
+Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
+Cu.import("resource://gre/modules/FileUtils.jsm", tmp);
+Cu.import("resource://gre/modules/osfile.jsm", tmp);
+var {PageThumbs, BackgroundPageThumbs, NewTabUtils, PageThumbsStorage, SessionStore, FileUtils, OS} = tmp;
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
+ "resource://testing-common/PlacesTestUtils.jsm");
+
+var oldEnabledPref = Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled");
+Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", false);
+
+registerCleanupFunction(function () {
+ while (gBrowser.tabs.length > 1)
+ gBrowser.removeTab(gBrowser.tabs[1]);
+ Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", oldEnabledPref)
+});
+
+/**
+ * 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.
+ */
+var TestRunner = {
+ /**
+ * Starts the test runner.
+ */
+ run: function () {
+ waitForExplicitFinish();
+
+ SessionStore.promiseInitialized.then(function () {
+ this._iter = runTests();
+ if (this._iter) {
+ this.next();
+ } else {
+ finish();
+ }
+ }.bind(this));
+ },
+
+ /**
+ * Runs the next available test or finishes if there's no test left.
+ * @param aValue This value will be passed to the yielder via the runner's
+ * iterator.
+ */
+ next: function (aValue) {
+ let obj = TestRunner._iter.next(aValue);
+ if (obj.done) {
+ finish();
+ return;
+ }
+
+ let value = obj.value || obj;
+ if (value && typeof value.then == "function") {
+ value.then(result => {
+ next(result);
+ }, error => {
+ ok(false, error + "\n" + error.stack);
+ });
+ }
+ }
+};
+
+/**
+ * Continues the current test execution.
+ * @param aValue This value will be passed to the yielder via the runner's
+ * iterator.
+ */
+function next(aValue) {
+ TestRunner.next(aValue);
+}
+
+/**
+ * Creates a new tab with the given URI.
+ * @param aURI The URI that's loaded in the tab.
+ * @param aCallback The function to call when the tab has loaded.
+ */
+function addTab(aURI, aCallback) {
+ let tab = gBrowser.selectedTab = gBrowser.addTab(aURI);
+ whenLoaded(tab.linkedBrowser, aCallback);
+}
+
+/**
+ * Loads a new URI into the currently selected tab.
+ * @param aURI The URI to load.
+ */
+function navigateTo(aURI) {
+ let browser = gBrowser.selectedBrowser;
+ whenLoaded(browser);
+ browser.loadURI(aURI);
+}
+
+/**
+ * Continues the current test execution when a load event for the given element
+ * has been received.
+ * @param aElement The DOM element to listen on.
+ * @param aCallback The function to call when the load event was dispatched.
+ */
+function whenLoaded(aElement, aCallback = next) {
+ aElement.addEventListener("load", function onLoad() {
+ aElement.removeEventListener("load", onLoad, true);
+ executeSoon(aCallback);
+ }, true);
+}
+
+/**
+ * Captures a screenshot for the currently selected tab, stores it in the cache,
+ * retrieves it from the cache and compares pixel color values.
+ * @param aRed The red component's intensity.
+ * @param aGreen The green component's intensity.
+ * @param aBlue The blue component's intensity.
+ * @param aMessage The info message to print when comparing the pixel color.
+ */
+function captureAndCheckColor(aRed, aGreen, aBlue, aMessage) {
+ let browser = gBrowser.selectedBrowser;
+ // We'll get oranges if the expiration filter removes the file during the
+ // test.
+ dontExpireThumbnailURLs([browser.currentURI.spec]);
+
+ // Capture the screenshot.
+ PageThumbs.captureAndStore(browser, function () {
+ retrieveImageDataForURL(browser.currentURI.spec, function ([r, g, b]) {
+ is("" + [r, g, b], "" + [aRed, aGreen, aBlue], aMessage);
+ next();
+ });
+ });
+}
+
+/**
+ * For a given URL, loads the corresponding thumbnail
+ * to a canvas and passes its image data to the callback.
+ * Note, not compat with e10s!
+ * @param aURL The url associated with the thumbnail.
+ * @param aCallback The function to pass the image data to.
+ */
+function retrieveImageDataForURL(aURL, aCallback) {
+ let width = 100, height = 100;
+ let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
+
+ let htmlns = "http://www.w3.org/1999/xhtml";
+ let img = document.createElementNS(htmlns, "img");
+ img.setAttribute("src", thumb);
+
+ whenLoaded(img, function () {
+ let canvas = document.createElementNS(htmlns, "canvas");
+ canvas.setAttribute("width", width);
+ canvas.setAttribute("height", height);
+
+ // Draw the image to a canvas and compare the pixel color values.
+ let ctx = canvas.getContext("2d");
+ ctx.drawImage(img, 0, 0, width, height);
+ let result = ctx.getImageData(0, 0, 100, 100).data;
+ aCallback(result);
+ });
+}
+
+/**
+ * Returns the file of the thumbnail with the given URL.
+ * @param aURL The URL of the thumbnail.
+ */
+function thumbnailFile(aURL) {
+ return new FileUtils.File(PageThumbsStorage.getFilePathForURL(aURL));
+}
+
+/**
+ * Checks if a thumbnail for the given URL exists.
+ * @param aURL The url associated to the thumbnail.
+ */
+function thumbnailExists(aURL) {
+ let file = thumbnailFile(aURL);
+ return file.exists() && file.fileSize;
+}
+
+/**
+ * Removes the thumbnail for the given URL.
+ * @param aURL The URL associated with the thumbnail.
+ */
+function removeThumbnail(aURL) {
+ let file = thumbnailFile(aURL);
+ file.remove(false);
+}
+
+/**
+ * Calls addVisits, and then forces the newtab module to repopulate its links.
+ * See addVisits for parameter descriptions.
+ */
+function addVisitsAndRepopulateNewTabLinks(aPlaceInfo, aCallback) {
+ PlacesTestUtils.addVisits(makeURI(aPlaceInfo)).then(() => {
+ NewTabUtils.links.populateCache(aCallback, true);
+ });
+}
+function promiseAddVisitsAndRepopulateNewTabLinks(aPlaceInfo) {
+ return new Promise(resolve => addVisitsAndRepopulateNewTabLinks(aPlaceInfo, resolve));
+}
+
+/**
+ * Calls a given callback when the thumbnail for a given URL has been found
+ * on disk. Keeps trying until the thumbnail has been created.
+ *
+ * @param aURL The URL of the thumbnail's page.
+ * @param [optional] aCallback
+ * Function to be invoked on completion.
+ */
+function whenFileExists(aURL, aCallback = next) {
+ let callback = aCallback;
+ if (!thumbnailExists(aURL)) {
+ callback = () => whenFileExists(aURL, aCallback);
+ }
+
+ executeSoon(callback);
+}
+
+/**
+ * Calls a given callback when the given file has been removed.
+ * Keeps trying until the file is removed.
+ *
+ * @param aFile The file that is being removed
+ * @param [optional] aCallback
+ * Function to be invoked on completion.
+ */
+function whenFileRemoved(aFile, aCallback) {
+ let callback = aCallback;
+ if (aFile.exists()) {
+ callback = () => whenFileRemoved(aFile, aCallback);
+ }
+
+ executeSoon(callback || next);
+}
+
+function wait(aMillis) {
+ setTimeout(next, aMillis);
+}
+
+/**
+ * Makes sure that a given list of URLs is not implicitly expired.
+ *
+ * @param aURLs The list of URLs that should not be expired.
+ */
+function dontExpireThumbnailURLs(aURLs) {
+ let dontExpireURLs = (cb) => cb(aURLs);
+ PageThumbs.addExpirationFilter(dontExpireURLs);
+
+ registerCleanupFunction(function () {
+ PageThumbs.removeExpirationFilter(dontExpireURLs);
+ });
+}
+
+function bgCapture(aURL, aOptions) {
+ bgCaptureWithMethod("capture", aURL, aOptions);
+}
+
+function bgCaptureIfMissing(aURL, aOptions) {
+ bgCaptureWithMethod("captureIfMissing", aURL, aOptions);
+}
+
+function bgCaptureWithMethod(aMethodName, aURL, aOptions = {}) {
+ // We'll get oranges if the expiration filter removes the file during the
+ // test.
+ dontExpireThumbnailURLs([aURL]);
+ if (!aOptions.onDone)
+ aOptions.onDone = next;
+ BackgroundPageThumbs[aMethodName](aURL, aOptions);
+}
+
+function bgTestPageURL(aOpts = {}) {
+ let TEST_PAGE_URL = "http://mochi.test:8888/browser/toolkit/components/thumbnails/test/thumbnails_background.sjs";
+ return TEST_PAGE_URL + "?" + encodeURIComponent(JSON.stringify(aOpts));
+}
+
+function bgAddPageThumbObserver(url) {
+ return new Promise((resolve, reject) => {
+ function observe(subject, topic, data) { // jshint ignore:line
+ if (data === url) {
+ switch (topic) {
+ case "page-thumbnail:create":
+ resolve();
+ break;
+ case "page-thumbnail:error":
+ reject(new Error("page-thumbnail:error"));
+ break;
+ }
+ Services.obs.removeObserver(observe, "page-thumbnail:create");
+ Services.obs.removeObserver(observe, "page-thumbnail:error");
+ }
+ }
+ Services.obs.addObserver(observe, "page-thumbnail:create", false);
+ Services.obs.addObserver(observe, "page-thumbnail:error", false);
+ });
+}
+
+function bgAddCrashObserver() {
+ let crashed = false;
+ Services.obs.addObserver(function crashObserver(subject, topic, data) {
+ is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
+ ok(subject instanceof Components.interfaces.nsIPropertyBag2,
+ 'Subject implements nsIPropertyBag2.');
+ // we might see this called as the process terminates due to previous tests.
+ // We are only looking for "abnormal" exits...
+ if (!subject.hasKey("abnormal")) {
+ info("This is a normal termination and isn't the one we are looking for...");
+ return;
+ }
+ Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
+ crashed = true;
+
+ var dumpID;
+ if ('nsICrashReporter' in Components.interfaces) {
+ dumpID = subject.getPropertyAsAString('dumpID');
+ ok(dumpID, "dumpID is present and not an empty string");
+ }
+
+ if (dumpID) {
+ var minidumpDirectory = getMinidumpDirectory();
+ removeFile(minidumpDirectory, dumpID + '.dmp');
+ removeFile(minidumpDirectory, dumpID + '.extra');
+ }
+ }, 'ipc:content-shutdown', false);
+ return {
+ get crashed() {
+ return crashed;
+ }
+ };
+}
+
+function bgInjectCrashContentScript() {
+ const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js";
+ let thumbnailBrowser = BackgroundPageThumbs._thumbBrowser;
+ let mm = thumbnailBrowser.messageManager;
+ mm.loadFrameScript(TEST_CONTENT_HELPER, false);
+ return mm;
+}
+
+function getMinidumpDirectory() {
+ var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
+ dir.append("minidumps");
+ return dir;
+}
+
+function removeFile(directory, filename) {
+ var file = directory.clone();
+ file.append(filename);
+ if (file.exists()) {
+ file.remove(false);
+ }
+}