diff options
Diffstat (limited to 'toolkit/components/thumbnails/test/head.js')
-rw-r--r-- | toolkit/components/thumbnails/test/head.js | 356 |
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); + } +} |