2012-01-19 07:01:43 -08:00
|
|
|
/* Any copyright is dedicated to the Public Domain.
|
|
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
|
2012-02-02 00:56:35 -08:00
|
|
|
let tmp = {};
|
2013-02-16 20:15:41 -08:00
|
|
|
Cu.import("resource://gre/modules/PageThumbs.jsm", tmp);
|
2013-11-21 17:38:17 -08:00
|
|
|
Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", tmp);
|
2014-01-07 12:59:18 -08:00
|
|
|
Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp);
|
2013-02-27 04:37:45 -08:00
|
|
|
Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
|
2013-03-27 08:35:56 -07:00
|
|
|
Cu.import("resource://gre/modules/FileUtils.jsm", tmp);
|
|
|
|
Cu.import("resource://gre/modules/osfile.jsm", tmp);
|
2014-01-07 12:59:18 -08:00
|
|
|
let {PageThumbs, BackgroundPageThumbs, NewTabUtils, PageThumbsStorage, SessionStore, FileUtils, OS} = tmp;
|
2012-01-19 07:01:43 -08:00
|
|
|
|
2013-01-24 20:55:22 -08:00
|
|
|
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
|
|
|
|
2013-08-06 18:10:58 -07:00
|
|
|
let oldEnabledPref = Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled");
|
2013-08-07 16:52:01 -07:00
|
|
|
Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", false);
|
2013-07-23 00:40:16 -07:00
|
|
|
|
2012-01-19 07:01:43 -08:00
|
|
|
registerCleanupFunction(function () {
|
|
|
|
while (gBrowser.tabs.length > 1)
|
|
|
|
gBrowser.removeTab(gBrowser.tabs[1]);
|
2013-08-06 18:10:58 -07:00
|
|
|
Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", oldEnabledPref)
|
2012-01-19 07:01:43 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
let TestRunner = {
|
|
|
|
/**
|
|
|
|
* Starts the test runner.
|
|
|
|
*/
|
|
|
|
run: function () {
|
|
|
|
waitForExplicitFinish();
|
2012-05-14 09:18:27 -07:00
|
|
|
|
2013-02-27 04:37:45 -08:00
|
|
|
SessionStore.promiseInitialized.then(function () {
|
|
|
|
this._iter = runTests();
|
|
|
|
if (this._iter) {
|
|
|
|
this.next();
|
|
|
|
} else {
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
}.bind(this));
|
2012-01-19 07:01:43 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Runs the next available test or finishes if there's no test left.
|
2013-11-21 17:38:17 -08:00
|
|
|
* @param aValue This value will be passed to the yielder via the runner's
|
|
|
|
* iterator.
|
2012-01-19 07:01:43 -08:00
|
|
|
*/
|
2013-11-21 17:38:17 -08:00
|
|
|
next: function (aValue) {
|
2012-01-19 07:01:43 -08:00
|
|
|
try {
|
2013-11-21 17:38:17 -08:00
|
|
|
let value = TestRunner._iter.send(aValue);
|
2013-08-19 16:26:33 -07:00
|
|
|
if (value && typeof value.then == "function") {
|
|
|
|
value.then(result => {
|
|
|
|
next(result);
|
|
|
|
}, error => {
|
|
|
|
ok(false, error + "\n" + error.stack);
|
|
|
|
});
|
|
|
|
}
|
2012-01-19 07:01:43 -08:00
|
|
|
} catch (e if e instanceof StopIteration) {
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Continues the current test execution.
|
2013-11-21 17:38:17 -08:00
|
|
|
* @param aValue This value will be passed to the yielder via the runner's
|
|
|
|
* iterator.
|
2012-01-19 07:01:43 -08:00
|
|
|
*/
|
2013-11-21 17:38:17 -08:00
|
|
|
function next(aValue) {
|
|
|
|
TestRunner.next(aValue);
|
2012-01-19 07:01:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new tab with the given URI.
|
|
|
|
* @param aURI The URI that's loaded in the tab.
|
2012-08-24 18:11:06 -07:00
|
|
|
* @param aCallback The function to call when the tab has loaded.
|
2012-01-19 07:01:43 -08:00
|
|
|
*/
|
2012-08-24 18:11:06 -07:00
|
|
|
function addTab(aURI, aCallback) {
|
2012-01-19 07:01:43 -08:00
|
|
|
let tab = gBrowser.selectedTab = gBrowser.addTab(aURI);
|
2012-08-24 18:11:06 -07:00
|
|
|
whenLoaded(tab.linkedBrowser, aCallback);
|
2012-01-19 07:01:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loads a new URI into the currently selected tab.
|
|
|
|
* @param aURI The URI to load.
|
|
|
|
*/
|
|
|
|
function navigateTo(aURI) {
|
|
|
|
let browser = gBrowser.selectedTab.linkedBrowser;
|
2012-01-27 02:22:35 -08:00
|
|
|
whenLoaded(browser);
|
2012-01-19 07:01:43 -08:00
|
|
|
browser.loadURI(aURI);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-01-27 02:22:35 -08:00
|
|
|
* 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.
|
2012-01-19 07:01:43 -08:00
|
|
|
*/
|
2013-03-27 08:35:56 -07:00
|
|
|
function whenLoaded(aElement, aCallback = next) {
|
2012-01-27 02:22:35 -08:00
|
|
|
aElement.addEventListener("load", function onLoad() {
|
|
|
|
aElement.removeEventListener("load", onLoad, true);
|
2013-03-27 08:35:56 -07:00
|
|
|
executeSoon(aCallback);
|
2012-01-19 07:01:43 -08:00
|
|
|
}, true);
|
|
|
|
}
|
|
|
|
|
2012-01-27 02:22:35 -08:00
|
|
|
/**
|
|
|
|
* 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) {
|
2012-02-09 02:12:15 -08:00
|
|
|
let browser = gBrowser.selectedBrowser;
|
2012-01-27 02:22:35 -08:00
|
|
|
|
2012-02-09 00:48:30 -08:00
|
|
|
// Capture the screenshot.
|
2012-02-09 02:12:15 -08:00
|
|
|
PageThumbs.captureAndStore(browser, function () {
|
2013-02-27 04:37:45 -08:00
|
|
|
retrieveImageDataForURL(browser.currentURI.spec, function ([r, g, b]) {
|
|
|
|
is("" + [r,g,b], "" + [aRed, aGreen, aBlue], aMessage);
|
|
|
|
next();
|
|
|
|
});
|
2012-03-23 11:12:43 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-02-27 04:37:45 -08:00
|
|
|
* For a given URL, loads the corresponding thumbnail
|
|
|
|
* to a canvas and passes its image data to the callback.
|
|
|
|
* @param aURL The url associated with the thumbnail.
|
|
|
|
* @param aCallback The function to pass the image data to.
|
2012-03-23 11:12:43 -07:00
|
|
|
*/
|
2013-02-27 04:37:45 -08:00
|
|
|
function retrieveImageDataForURL(aURL, aCallback) {
|
2012-03-23 11:12:43 -07:00
|
|
|
let width = 100, height = 100;
|
|
|
|
let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
|
2013-08-19 16:26:33 -07:00
|
|
|
// create a tab with a chrome:// URL so it can host the thumbnail image.
|
|
|
|
// Note that we tried creating the element directly in the top-level chrome
|
|
|
|
// document, but this caused a strange problem:
|
|
|
|
// * call this with the url of an image.
|
|
|
|
// * immediately change the image content.
|
|
|
|
// * call this again with the same url (now holding different content)
|
|
|
|
// The original image data would be used. Maybe the img hadn't been
|
|
|
|
// collected yet and the platform noticed the same URL, so reused the
|
|
|
|
// content? Not sure - but this solves the problem.
|
|
|
|
addTab("chrome://global/content/mozilla.xhtml", () => {
|
|
|
|
let doc = gBrowser.selectedBrowser.contentDocument;
|
|
|
|
let htmlns = "http://www.w3.org/1999/xhtml";
|
|
|
|
let img = doc.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;
|
|
|
|
gBrowser.removeTab(gBrowser.selectedTab);
|
|
|
|
aCallback(result);
|
|
|
|
});
|
2012-05-14 23:10:55 -07:00
|
|
|
});
|
2012-01-27 02:22:35 -08:00
|
|
|
}
|
|
|
|
|
2013-11-21 17:38:17 -08:00
|
|
|
/**
|
|
|
|
* 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));
|
|
|
|
}
|
|
|
|
|
2012-09-28 01:42:32 -07:00
|
|
|
/**
|
|
|
|
* Checks if a thumbnail for the given URL exists.
|
|
|
|
* @param aURL The url associated to the thumbnail.
|
|
|
|
*/
|
|
|
|
function thumbnailExists(aURL) {
|
2013-11-21 17:38:17 -08:00
|
|
|
let file = thumbnailFile(aURL);
|
2012-09-28 01:42:32 -07:00
|
|
|
return file.exists() && file.fileSize;
|
|
|
|
}
|
2013-01-24 20:55:22 -08:00
|
|
|
|
2013-11-21 17:38:17 -08:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2013-01-24 20:55:22 -08:00
|
|
|
/**
|
|
|
|
* Asynchronously adds visits to a page, invoking a callback function when done.
|
|
|
|
*
|
|
|
|
* @param aPlaceInfo
|
2014-01-07 12:59:18 -08:00
|
|
|
* One of the following: a string spec, an nsIURI, an object describing
|
|
|
|
* the Place as described below, or an array of any such types. An
|
|
|
|
* object describing a Place must look like this:
|
2013-01-24 20:55:22 -08:00
|
|
|
* { uri: nsIURI of the page,
|
2014-01-07 12:59:18 -08:00
|
|
|
* [optional] transition: one of the TRANSITION_* from
|
|
|
|
* nsINavHistoryService,
|
2013-01-24 20:55:22 -08:00
|
|
|
* [optional] title: title of the page,
|
|
|
|
* [optional] visitDate: visit date in microseconds from the epoch
|
|
|
|
* [optional] referrer: nsIURI of the referrer for this visit
|
|
|
|
* }
|
|
|
|
* @param [optional] aCallback
|
|
|
|
* Function to be invoked on completion.
|
|
|
|
*/
|
|
|
|
function addVisits(aPlaceInfo, aCallback) {
|
|
|
|
let places = [];
|
|
|
|
if (aPlaceInfo instanceof Ci.nsIURI) {
|
|
|
|
places.push({ uri: aPlaceInfo });
|
|
|
|
}
|
|
|
|
else if (Array.isArray(aPlaceInfo)) {
|
|
|
|
places = places.concat(aPlaceInfo);
|
|
|
|
} else {
|
|
|
|
places.push(aPlaceInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create mozIVisitInfo for each entry.
|
|
|
|
let now = Date.now();
|
|
|
|
for (let i = 0; i < places.length; i++) {
|
2014-01-07 12:59:18 -08:00
|
|
|
if (typeof(places[i] == "string")) {
|
|
|
|
places[i] = { uri: Services.io.newURI(places[i], "", null) };
|
|
|
|
}
|
2013-01-24 20:55:22 -08:00
|
|
|
if (!places[i].title) {
|
|
|
|
places[i].title = "test visit for " + places[i].uri.spec;
|
|
|
|
}
|
|
|
|
places[i].visits = [{
|
|
|
|
transitionType: places[i].transition === undefined ? PlacesUtils.history.TRANSITION_LINK
|
|
|
|
: places[i].transition,
|
|
|
|
visitDate: places[i].visitDate || (now++) * 1000,
|
|
|
|
referrerURI: places[i].referrer
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
PlacesUtils.asyncHistory.updatePlaces(
|
|
|
|
places,
|
|
|
|
{
|
|
|
|
handleError: function AAV_handleError() {
|
|
|
|
throw("Unexpected error in adding visit.");
|
|
|
|
},
|
|
|
|
handleResult: function () {},
|
|
|
|
handleCompletion: function UP_handleCompletion() {
|
|
|
|
if (aCallback)
|
|
|
|
aCallback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-01-07 12:59:18 -08:00
|
|
|
/**
|
|
|
|
* Calls addVisits, and then forces the newtab module to repopulate its links.
|
|
|
|
* See addVisits for parameter descriptions.
|
|
|
|
*/
|
|
|
|
function addVisitsAndRepopulateNewTabLinks(aPlaceInfo, aCallback) {
|
|
|
|
addVisits(aPlaceInfo, () => NewTabUtils.links.populateCache(aCallback, true));
|
|
|
|
}
|
|
|
|
|
2013-02-11 06:47:22 -08:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-03-27 08:35:56 -07:00
|
|
|
function whenFileExists(aURL, aCallback = next) {
|
2013-02-11 06:47:22 -08:00
|
|
|
let callback = aCallback;
|
|
|
|
if (!thumbnailExists(aURL)) {
|
|
|
|
callback = function () whenFileExists(aURL, aCallback);
|
|
|
|
}
|
|
|
|
|
2013-03-27 08:35:56 -07:00
|
|
|
executeSoon(callback);
|
2013-02-11 06:47:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 = function () whenFileRemoved(aFile, aCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
executeSoon(callback || next);
|
|
|
|
}
|
2013-04-10 09:39:50 -07:00
|
|
|
|
2013-11-21 17:38:17 -08:00
|
|
|
function wait(aMillis) {
|
|
|
|
setTimeout(next, aMillis);
|
|
|
|
}
|
|
|
|
|
2013-04-10 09:39:50 -07:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
});
|
|
|
|
}
|
2013-11-21 17:38:17 -08:00
|
|
|
|
|
|
|
function bgCapture(aURL, aOptions) {
|
|
|
|
bgCaptureWithMethod("capture", aURL, aOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
function bgCaptureIfMissing(aURL, aOptions) {
|
|
|
|
bgCaptureWithMethod("captureIfMissing", aURL, aOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
function bgCaptureWithMethod(aMethodName, aURL, aOptions = {}) {
|
2013-12-09 19:52:51 -08:00
|
|
|
if (!aOptions.onDone)
|
|
|
|
aOptions.onDone = next;
|
2013-11-21 17:38:17 -08:00
|
|
|
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));
|
|
|
|
}
|
2013-12-09 19:52:51 -08:00
|
|
|
|
|
|
|
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() 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);
|
|
|
|
}
|
|
|
|
}
|