summaryrefslogtreecommitdiffstats
path: root/browser/modules/test/xpcshell
diff options
context:
space:
mode:
Diffstat (limited to 'browser/modules/test/xpcshell')
-rw-r--r--browser/modules/test/xpcshell/.eslintrc.js7
-rw-r--r--browser/modules/test/xpcshell/test_AttributionCode.js110
-rw-r--r--browser/modules/test/xpcshell/test_DirectoryLinksProvider.js1854
-rw-r--r--browser/modules/test/xpcshell/test_LaterRun.js138
-rw-r--r--browser/modules/test/xpcshell/test_SitePermissions.js115
-rw-r--r--browser/modules/test/xpcshell/xpcshell.ini11
6 files changed, 2235 insertions, 0 deletions
diff --git a/browser/modules/test/xpcshell/.eslintrc.js b/browser/modules/test/xpcshell/.eslintrc.js
new file mode 100644
index 000000000..fee088c17
--- /dev/null
+++ b/browser/modules/test/xpcshell/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/browser/modules/test/xpcshell/test_AttributionCode.js b/browser/modules/test/xpcshell/test_AttributionCode.js
new file mode 100644
index 000000000..d979ae845
--- /dev/null
+++ b/browser/modules/test/xpcshell/test_AttributionCode.js
@@ -0,0 +1,110 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+"use strict";
+
+const {interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource:///modules/AttributionCode.jsm");
+Cu.import('resource://gre/modules/osfile.jsm');
+Cu.import("resource://gre/modules/Services.jsm");
+
+let validAttrCodes = [
+ {code: "source%3Dgoogle.com%26medium%3Dorganic%26campaign%3D(not%20set)%26content%3D(not%20set)",
+ parsed: {"source": "google.com", "medium": "organic",
+ "campaign": "(not%20set)", "content": "(not%20set)"}},
+ {code: "source%3Dgoogle.com%26medium%3Dorganic%26campaign%3D%26content%3D",
+ parsed: {"source": "google.com", "medium": "organic"}},
+ {code: "source%3Dgoogle.com%26medium%3Dorganic%26campaign%3D(not%20set)",
+ parsed: {"source": "google.com", "medium": "organic", "campaign": "(not%20set)"}},
+ {code: "source%3Dgoogle.com%26medium%3Dorganic",
+ parsed: {"source": "google.com", "medium": "organic"}},
+ {code: "source%3Dgoogle.com",
+ parsed: {"source": "google.com"}},
+ {code: "medium%3Dgoogle.com",
+ parsed: {"medium": "google.com"}},
+ {code: "campaign%3Dgoogle.com",
+ parsed: {"campaign": "google.com"}},
+ {code: "content%3Dgoogle.com",
+ parsed: {"content": "google.com"}}
+];
+
+let invalidAttrCodes = [
+ // Empty string
+ "",
+ // Not escaped
+ "source=google.com&medium=organic&campaign=(not set)&content=(not set)",
+ // Too long
+ "source%3Dreallyreallyreallyreallyreallyreallyreallyreallyreallylongdomain.com%26medium%3Dorganic%26campaign%3D(not%20set)%26content%3Dalmostexactlyenoughcontenttomakethisstringlongerthanthe200characterlimit",
+ // Unknown key name
+ "source%3Dgoogle.com%26medium%3Dorganic%26large%3Dgeneticallymodified",
+ // Empty key name
+ "source%3Dgoogle.com%26medium%3Dorganic%26%3Dgeneticallymodified"
+];
+
+function* writeAttributionFile(data) {
+ let appDir = Services.dirsvc.get("LocalAppData", Ci.nsIFile);
+ let file = appDir.clone();
+ file.append(Services.appinfo.vendor || "mozilla");
+ file.append(AppConstants.MOZ_APP_NAME);
+
+ yield OS.File.makeDir(file.path,
+ {from: appDir.path, ignoreExisting: true});
+
+ file.append("postSigningData");
+ yield OS.File.writeAtomic(file.path, data);
+}
+
+/**
+ * Test validation of attribution codes,
+ * to make sure we reject bad ones and accept good ones.
+ */
+add_task(function* testValidAttrCodes() {
+ for (let entry of validAttrCodes) {
+ AttributionCode._clearCache();
+ yield writeAttributionFile(entry.code);
+ let result = yield AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, entry.parsed,
+ "Parsed code should match expected value, code was: " + entry.code);
+ }
+ AttributionCode._clearCache();
+});
+
+/**
+ * Make sure codes with various formatting errors are not seen as valid.
+ */
+add_task(function* testInvalidAttrCodes() {
+ for (let code of invalidAttrCodes) {
+ AttributionCode._clearCache();
+ yield writeAttributionFile(code);
+ let result = yield AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, {},
+ "Code should have failed to parse: " + code);
+ }
+ AttributionCode._clearCache();
+});
+
+/**
+ * Test the cache by deleting the attribution data file
+ * and making sure we still get the expected code.
+ */
+add_task(function* testDeletedFile() {
+ // Set up the test by clearing the cache and writing a valid file.
+ yield writeAttributionFile(validAttrCodes[0].code);
+ let result = yield AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, validAttrCodes[0].parsed,
+ "The code should be readable directly from the file");
+
+ // Delete the file and make sure we can still read the value back from cache.
+ yield AttributionCode.deleteFileAsync();
+ result = yield AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, validAttrCodes[0].parsed,
+ "The code should be readable from the cache");
+
+ // Clear the cache and check we can't read anything.
+ AttributionCode._clearCache();
+ result = yield AttributionCode.getAttrDataAsync();
+ Assert.deepEqual(result, {},
+ "Shouldn't be able to get a code after file is deleted and cache is cleared");
+});
diff --git a/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js b/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js
new file mode 100644
index 000000000..712f52fa6
--- /dev/null
+++ b/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js
@@ -0,0 +1,1854 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+"use strict";
+
+/**
+ * This file tests the DirectoryLinksProvider singleton in the DirectoryLinksProvider.jsm module.
+ */
+
+var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu, Constructor: CC } = Components;
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource:///modules/DirectoryLinksProvider.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Http.jsm");
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
+ "resource://gre/modules/NewTabUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
+ "resource://testing-common/PlacesTestUtils.jsm");
+
+do_get_profile();
+
+const DIRECTORY_LINKS_FILE = "directoryLinks.json";
+const DIRECTORY_FRECENCY = 1000;
+const SUGGESTED_FRECENCY = Infinity;
+const kURLData = {"directory": [{"url":"http://example.com", "title":"LocalSource"}]};
+const kTestURL = 'data:application/json,' + JSON.stringify(kURLData);
+
+// DirectoryLinksProvider preferences
+const kLocalePref = DirectoryLinksProvider._observedPrefs.prefSelectedLocale;
+const kSourceUrlPref = DirectoryLinksProvider._observedPrefs.linksURL;
+const kPingUrlPref = "browser.newtabpage.directory.ping";
+const kNewtabEnhancedPref = "browser.newtabpage.enhanced";
+
+// httpd settings
+var server;
+const kDefaultServerPort = 9000;
+const kBaseUrl = "http://localhost:" + kDefaultServerPort;
+const kExamplePath = "/exampleTest/";
+const kFailPath = "/fail/";
+const kPingPath = "/ping/";
+const kExampleURL = kBaseUrl + kExamplePath;
+const kFailURL = kBaseUrl + kFailPath;
+const kPingUrl = kBaseUrl + kPingPath;
+
+// app/profile/firefox.js are not avaialble in xpcshell: hence, preset them
+Services.prefs.setCharPref(kLocalePref, "en-US");
+Services.prefs.setCharPref(kSourceUrlPref, kTestURL);
+Services.prefs.setCharPref(kPingUrlPref, kPingUrl);
+Services.prefs.setBoolPref(kNewtabEnhancedPref, true);
+
+const kHttpHandlerData = {};
+kHttpHandlerData[kExamplePath] = {"directory": [{"url":"http://example.com", "title":"RemoteSource"}]};
+
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream");
+
+var gLastRequestPath;
+
+var suggestedTile1 = {
+ url: "http://turbotax.com",
+ type: "affiliate",
+ lastVisitDate: 4,
+ adgroup_name: "Adgroup1",
+ frecent_sites: [
+ "taxact.com",
+ "hrblock.com",
+ "1040.com",
+ "taxslayer.com"
+ ]
+};
+var suggestedTile2 = {
+ url: "http://irs.gov",
+ type: "affiliate",
+ lastVisitDate: 3,
+ adgroup_name: "Adgroup2",
+ frecent_sites: [
+ "taxact.com",
+ "hrblock.com",
+ "freetaxusa.com",
+ "taxslayer.com"
+ ]
+};
+var suggestedTile3 = {
+ url: "http://hrblock.com",
+ type: "affiliate",
+ lastVisitDate: 2,
+ adgroup_name: "Adgroup3",
+ frecent_sites: [
+ "taxact.com",
+ "freetaxusa.com",
+ "1040.com",
+ "taxslayer.com"
+ ]
+};
+var suggestedTile4 = {
+ url: "http://sponsoredtile.com",
+ type: "sponsored",
+ lastVisitDate: 1,
+ adgroup_name: "Adgroup4",
+ frecent_sites: [
+ "sponsoredtarget.com"
+ ]
+}
+var suggestedTile5 = {
+ url: "http://eviltile.com",
+ type: "affiliate",
+ lastVisitDate: 5,
+ explanation: "This is an evil tile <form><button formaction='javascript:alert(1)''>X</button></form> muhahaha",
+ adgroup_name: "WE ARE EVIL <link rel='import' href='test.svg'/>",
+ frecent_sites: [
+ "eviltarget.com"
+ ]
+}
+var someOtherSite = {url: "http://someothersite.com", title: "Not_A_Suggested_Site"};
+
+function getHttpHandler(path) {
+ let code = 200;
+ let body = JSON.stringify(kHttpHandlerData[path]);
+ if (path == kFailPath) {
+ code = 204;
+ }
+ return function(aRequest, aResponse) {
+ gLastRequestPath = aRequest.path;
+ aResponse.setStatusLine(null, code);
+ aResponse.setHeader("Content-Type", "application/json");
+ aResponse.write(body);
+ };
+}
+
+function isIdentical(actual, expected) {
+ if (expected == null) {
+ do_check_eq(actual, expected);
+ }
+ else if (typeof expected == "object") {
+ // Make sure all the keys match up
+ do_check_eq(Object.keys(actual).sort() + "", Object.keys(expected).sort());
+
+ // Recursively check each value individually
+ Object.keys(expected).forEach(key => {
+ isIdentical(actual[key], expected[key]);
+ });
+ }
+ else {
+ do_check_eq(actual, expected);
+ }
+}
+
+function fetchData() {
+ let deferred = Promise.defer();
+
+ DirectoryLinksProvider.getLinks(linkData => {
+ deferred.resolve(linkData);
+ });
+ return deferred.promise;
+}
+
+function readJsonFile(jsonFile = DIRECTORY_LINKS_FILE) {
+ let decoder = new TextDecoder();
+ let directoryLinksFilePath = OS.Path.join(OS.Constants.Path.localProfileDir, jsonFile);
+ return OS.File.read(directoryLinksFilePath).then(array => {
+ let json = decoder.decode(array);
+ return JSON.parse(json);
+ }, () => { return "" });
+}
+
+function cleanJsonFile(jsonFile = DIRECTORY_LINKS_FILE) {
+ let directoryLinksFilePath = OS.Path.join(OS.Constants.Path.localProfileDir, jsonFile);
+ return OS.File.remove(directoryLinksFilePath);
+}
+
+function LinksChangeObserver() {
+ this.deferred = Promise.defer();
+ this.onManyLinksChanged = () => this.deferred.resolve();
+ this.onDownloadFail = this.onManyLinksChanged;
+}
+
+function promiseDirectoryDownloadOnPrefChange(pref, newValue) {
+ let oldValue = Services.prefs.getCharPref(pref);
+ if (oldValue != newValue) {
+ // if the preference value is already equal to newValue
+ // the pref service will not call our observer and we
+ // deadlock. Hence only setup observer if values differ
+ let observer = new LinksChangeObserver();
+ DirectoryLinksProvider.addObserver(observer);
+ Services.prefs.setCharPref(pref, newValue);
+ return observer.deferred.promise.then(() => {
+ DirectoryLinksProvider.removeObserver(observer);
+ });
+ }
+ return Promise.resolve();
+}
+
+function promiseSetupDirectoryLinksProvider(options = {}) {
+ return Task.spawn(function*() {
+ let linksURL = options.linksURL || kTestURL;
+ yield DirectoryLinksProvider.init();
+ yield promiseDirectoryDownloadOnPrefChange(kLocalePref, options.locale || "en-US");
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, linksURL);
+ do_check_eq(DirectoryLinksProvider._linksURL, linksURL);
+ DirectoryLinksProvider._lastDownloadMS = options.lastDownloadMS || 0;
+ });
+}
+
+function promiseCleanDirectoryLinksProvider() {
+ return Task.spawn(function*() {
+ yield promiseDirectoryDownloadOnPrefChange(kLocalePref, "en-US");
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, kTestURL);
+ yield DirectoryLinksProvider._clearFrequencyCap();
+ yield DirectoryLinksProvider._loadInadjacentSites();
+ DirectoryLinksProvider._lastDownloadMS = 0;
+ DirectoryLinksProvider.reset();
+ });
+}
+
+function run_test() {
+ // Set up a mock HTTP server to serve a directory page
+ server = new HttpServer();
+ server.registerPrefixHandler(kExamplePath, getHttpHandler(kExamplePath));
+ server.registerPrefixHandler(kFailPath, getHttpHandler(kFailPath));
+ server.start(kDefaultServerPort);
+ NewTabUtils.init();
+
+ run_next_test();
+
+ // Teardown.
+ do_register_cleanup(function() {
+ server.stop(function() { });
+ DirectoryLinksProvider.reset();
+ Services.prefs.clearUserPref(kLocalePref);
+ Services.prefs.clearUserPref(kSourceUrlPref);
+ Services.prefs.clearUserPref(kPingUrlPref);
+ Services.prefs.clearUserPref(kNewtabEnhancedPref);
+ });
+}
+
+
+function setTimeout(fun, timeout) {
+ let timer = Components.classes["@mozilla.org/timer;1"]
+ .createInstance(Components.interfaces.nsITimer);
+ var event = {
+ notify: function () {
+ fun();
+ }
+ };
+ timer.initWithCallback(event, timeout,
+ Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+ return timer;
+}
+
+add_task(function test_shouldUpdateSuggestedTile() {
+ let suggestedLink = {
+ targetedSite: "somesite.com"
+ };
+
+ // DirectoryLinksProvider has no suggested tile and no top sites => no need to update
+ do_check_eq(DirectoryLinksProvider._getCurrentTopSiteCount(), 0);
+ isIdentical(NewTabUtils.getProviderLinks(), []);
+ do_check_eq(DirectoryLinksProvider._shouldUpdateSuggestedTile(), false);
+
+ // DirectoryLinksProvider has a suggested tile and no top sites => need to update
+ let origGetProviderLinks = NewTabUtils.getProviderLinks;
+ NewTabUtils.getProviderLinks = (provider) => [suggestedLink];
+
+ do_check_eq(DirectoryLinksProvider._getCurrentTopSiteCount(), 0);
+ isIdentical(NewTabUtils.getProviderLinks(), [suggestedLink]);
+ do_check_eq(DirectoryLinksProvider._shouldUpdateSuggestedTile(), true);
+
+ // DirectoryLinksProvider has a suggested tile and 8 top sites => no need to update
+ let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+ DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
+
+ do_check_eq(DirectoryLinksProvider._getCurrentTopSiteCount(), 8);
+ isIdentical(NewTabUtils.getProviderLinks(), [suggestedLink]);
+ do_check_eq(DirectoryLinksProvider._shouldUpdateSuggestedTile(), false);
+
+ // DirectoryLinksProvider has no suggested tile and 8 top sites => need to update
+ NewTabUtils.getProviderLinks = origGetProviderLinks;
+ do_check_eq(DirectoryLinksProvider._getCurrentTopSiteCount(), 8);
+ isIdentical(NewTabUtils.getProviderLinks(), []);
+ do_check_eq(DirectoryLinksProvider._shouldUpdateSuggestedTile(), true);
+
+ // Cleanup
+ DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+});
+
+add_task(function* test_updateSuggestedTile() {
+ let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
+
+ // Initial setup
+ let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+
+ let testObserver = new TestFirstRun();
+ DirectoryLinksProvider.addObserver(testObserver);
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+ let links = yield fetchData();
+
+ let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+ NewTabUtils.isTopPlacesSite = function(site) {
+ return topSites.indexOf(site) >= 0;
+ }
+
+ let origGetProviderLinks = NewTabUtils.getProviderLinks;
+ NewTabUtils.getProviderLinks = function(provider) {
+ return links;
+ }
+
+ let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+ DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
+
+ do_check_eq(DirectoryLinksProvider._updateSuggestedTile(), undefined);
+
+ function TestFirstRun() {
+ this.promise = new Promise(resolve => {
+ this.onLinkChanged = (directoryLinksProvider, link) => {
+ links.unshift(link);
+ let possibleLinks = [suggestedTile1.url, suggestedTile2.url, suggestedTile3.url];
+
+ isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], ["hrblock.com", "1040.com", "freetaxusa.com"]);
+ do_check_true(possibleLinks.indexOf(link.url) > -1);
+ do_check_eq(link.frecency, SUGGESTED_FRECENCY);
+ do_check_eq(link.type, "affiliate");
+ resolve();
+ };
+ });
+ }
+
+ function TestChangingSuggestedTile() {
+ this.count = 0;
+ this.promise = new Promise(resolve => {
+ this.onLinkChanged = (directoryLinksProvider, link) => {
+ this.count++;
+ let possibleLinks = [suggestedTile1.url, suggestedTile2.url, suggestedTile3.url];
+
+ do_check_true(possibleLinks.indexOf(link.url) > -1);
+ do_check_eq(link.type, "affiliate");
+ do_check_true(this.count <= 2);
+
+ if (this.count == 1) {
+ // The removed suggested link is the one we added initially.
+ do_check_eq(link.url, links.shift().url);
+ do_check_eq(link.frecency, SUGGESTED_FRECENCY);
+ } else {
+ links.unshift(link);
+ do_check_eq(link.frecency, SUGGESTED_FRECENCY);
+ }
+ isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], ["hrblock.com", "freetaxusa.com"]);
+ resolve();
+ }
+ });
+ }
+
+ function TestRemovingSuggestedTile() {
+ this.count = 0;
+ this.promise = new Promise(resolve => {
+ this.onLinkChanged = (directoryLinksProvider, link) => {
+ this.count++;
+
+ do_check_eq(link.type, "affiliate");
+ do_check_eq(this.count, 1);
+ do_check_eq(link.frecency, SUGGESTED_FRECENCY);
+ do_check_eq(link.url, links.shift().url);
+ isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], []);
+ resolve();
+ }
+ });
+ }
+
+ // Test first call to '_updateSuggestedTile()', called when fetching directory links.
+ yield testObserver.promise;
+ DirectoryLinksProvider.removeObserver(testObserver);
+
+ // Removing a top site that doesn't have a suggested link should
+ // not change the current suggested tile.
+ let removedTopsite = topSites.shift();
+ do_check_eq(removedTopsite, "site0.com");
+ do_check_false(NewTabUtils.isTopPlacesSite(removedTopsite));
+ let updateSuggestedTile = DirectoryLinksProvider._handleLinkChanged({
+ url: "http://" + removedTopsite,
+ type: "history",
+ });
+ do_check_false(updateSuggestedTile);
+
+ // Removing a top site that has a suggested link should
+ // remove any current suggested tile and add a new one.
+ testObserver = new TestChangingSuggestedTile();
+ DirectoryLinksProvider.addObserver(testObserver);
+ removedTopsite = topSites.shift();
+ do_check_eq(removedTopsite, "1040.com");
+ do_check_false(NewTabUtils.isTopPlacesSite(removedTopsite));
+ DirectoryLinksProvider.onLinkChanged(DirectoryLinksProvider, {
+ url: "http://" + removedTopsite,
+ type: "history",
+ });
+ yield testObserver.promise;
+ do_check_eq(testObserver.count, 2);
+ DirectoryLinksProvider.removeObserver(testObserver);
+
+ // Removing all top sites with suggested links should remove
+ // the current suggested link and not replace it.
+ topSites = [];
+ testObserver = new TestRemovingSuggestedTile();
+ DirectoryLinksProvider.addObserver(testObserver);
+ DirectoryLinksProvider.onManyLinksChanged();
+ yield testObserver.promise;
+
+ // Cleanup
+ yield promiseCleanDirectoryLinksProvider();
+ NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+ NewTabUtils.getProviderLinks = origGetProviderLinks;
+ DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+});
+
+add_task(function* test_suggestedLinksMap() {
+ let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3, suggestedTile4], "directory": [someOtherSite]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+ let links = yield fetchData();
+
+ // Ensure the suggested tiles were not considered directory tiles.
+ do_check_eq(links.length, 1);
+ let expected_data = [{url: "http://someothersite.com", title: "Not_A_Suggested_Site", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
+ isIdentical(links, expected_data);
+
+ // Check for correctly saved suggested tiles data.
+ expected_data = {
+ "taxact.com": [suggestedTile1, suggestedTile2, suggestedTile3],
+ "hrblock.com": [suggestedTile1, suggestedTile2],
+ "1040.com": [suggestedTile1, suggestedTile3],
+ "taxslayer.com": [suggestedTile1, suggestedTile2, suggestedTile3],
+ "freetaxusa.com": [suggestedTile2, suggestedTile3],
+ "sponsoredtarget.com": [suggestedTile4],
+ };
+
+ let suggestedSites = [...DirectoryLinksProvider._suggestedLinks.keys()];
+ do_check_eq(suggestedSites.indexOf("sponsoredtarget.com"), 5);
+ do_check_eq(suggestedSites.length, Object.keys(expected_data).length);
+
+ DirectoryLinksProvider._suggestedLinks.forEach((suggestedLinks, site) => {
+ let suggestedLinksItr = suggestedLinks.values();
+ for (let link of expected_data[site]) {
+ let linkCopy = JSON.parse(JSON.stringify(link));
+ linkCopy.targetedName = link.adgroup_name;
+ linkCopy.explanation = "";
+ isIdentical(suggestedLinksItr.next().value, linkCopy);
+ }
+ })
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_topSitesWithSuggestedLinks() {
+ let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
+ let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+ NewTabUtils.isTopPlacesSite = function(site) {
+ return topSites.indexOf(site) >= 0;
+ }
+
+ // Mock out getProviderLinks() so we don't have to populate cache in NewTabUtils
+ let origGetProviderLinks = NewTabUtils.getProviderLinks;
+ NewTabUtils.getProviderLinks = function(provider) {
+ return [];
+ }
+
+ // We start off with no top sites with suggested links.
+ do_check_eq(DirectoryLinksProvider._topSitesWithSuggestedLinks.size, 0);
+
+ let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+ yield fetchData();
+
+ // Check we've populated suggested links as expected.
+ do_check_eq(DirectoryLinksProvider._suggestedLinks.size, 5);
+
+ // When many sites change, we update _topSitesWithSuggestedLinks as expected.
+ let expectedTopSitesWithSuggestedLinks = ["hrblock.com", "1040.com", "freetaxusa.com"];
+ DirectoryLinksProvider._handleManyLinksChanged();
+ isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
+
+ // Removing site6.com as a topsite has no impact on _topSitesWithSuggestedLinks.
+ let popped = topSites.pop();
+ DirectoryLinksProvider._handleLinkChanged({url: "http://" + popped});
+ isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
+
+ // Removing freetaxusa.com as a topsite will remove it from _topSitesWithSuggestedLinks.
+ popped = topSites.pop();
+ expectedTopSitesWithSuggestedLinks.pop();
+ DirectoryLinksProvider._handleLinkChanged({url: "http://" + popped});
+ isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
+
+ // Re-adding freetaxusa.com as a topsite will add it to _topSitesWithSuggestedLinks.
+ topSites.push(popped);
+ expectedTopSitesWithSuggestedLinks.push(popped);
+ DirectoryLinksProvider._handleLinkChanged({url: "http://" + popped});
+ isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
+
+ // Cleanup.
+ NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+ NewTabUtils.getProviderLinks = origGetProviderLinks;
+});
+
+add_task(function* test_suggestedAttributes() {
+ let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+ NewTabUtils.isTopPlacesSite = () => true;
+
+ let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+ DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
+
+ let frecent_sites = "addons.mozilla.org,air.mozilla.org,blog.mozilla.org,bugzilla.mozilla.org,developer.mozilla.org,etherpad.mozilla.org,forums.mozillazine.org,hacks.mozilla.org,hg.mozilla.org,mozilla.org,planet.mozilla.org,quality.mozilla.org,support.mozilla.org,treeherder.mozilla.org,wiki.mozilla.org".split(",");
+ let imageURI = "https://image/";
+ let title = "the title";
+ let type = "affiliate";
+ let url = "http://test.url/";
+ let adgroup_name = "Mozilla";
+ let data = {
+ suggested: [{
+ frecent_sites,
+ imageURI,
+ title,
+ type,
+ url,
+ adgroup_name
+ }]
+ };
+ let dataURI = "data:application/json," + escape(JSON.stringify(data));
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ // Wait for links to get loaded
+ let gLinks = NewTabUtils.links;
+ gLinks.addProvider(DirectoryLinksProvider);
+ gLinks.populateCache();
+ yield new Promise(resolve => {
+ NewTabUtils.allPages.register({
+ observe: _ => _,
+ update() {
+ NewTabUtils.allPages.unregister(this);
+ resolve();
+ }
+ });
+ });
+
+ // Make sure we get the expected attributes on the suggested tile
+ let link = gLinks.getLinks()[0];
+ do_check_eq(link.imageURI, imageURI);
+ do_check_eq(link.targetedName, "Mozilla");
+ do_check_eq(link.targetedSite, frecent_sites[0]);
+ do_check_eq(link.title, title);
+ do_check_eq(link.type, type);
+ do_check_eq(link.url, url);
+
+ // Cleanup.
+ NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+ DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+ gLinks.removeProvider(DirectoryLinksProvider);
+ DirectoryLinksProvider.removeObserver(gLinks);
+});
+
+add_task(function* test_frequencyCappedSites_views() {
+ Services.prefs.setCharPref(kPingUrlPref, "");
+ let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+ NewTabUtils.isTopPlacesSite = () => true;
+
+ let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+ DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
+
+ let testUrl = "http://frequency.capped/link";
+ let targets = ["top.site.com"];
+ let data = {
+ suggested: [{
+ type: "affiliate",
+ frecent_sites: targets,
+ url: testUrl,
+ frequency_caps: {daily: 5},
+ adgroup_name: "Test"
+ }],
+ directory: [{
+ type: "organic",
+ url: "http://directory.site/"
+ }]
+ };
+ let dataURI = "data:application/json," + JSON.stringify(data);
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ // Wait for links to get loaded
+ let gLinks = NewTabUtils.links;
+ gLinks.addProvider(DirectoryLinksProvider);
+ gLinks.populateCache();
+ yield new Promise(resolve => {
+ NewTabUtils.allPages.register({
+ observe: _ => _,
+ update() {
+ NewTabUtils.allPages.unregister(this);
+ resolve();
+ }
+ });
+ });
+
+ function synthesizeAction(action) {
+ DirectoryLinksProvider.reportSitesAction([{
+ link: {
+ targetedSite: targets[0],
+ url: testUrl
+ }
+ }], action, 0);
+ }
+
+ function checkFirstTypeAndLength(type, length) {
+ let links = gLinks.getLinks();
+ do_check_eq(links[0].type, type);
+ do_check_eq(links.length, length);
+ }
+
+ // Make sure we get 5 views of the link before it is removed
+ checkFirstTypeAndLength("affiliate", 2);
+ synthesizeAction("view");
+ checkFirstTypeAndLength("affiliate", 2);
+ synthesizeAction("view");
+ checkFirstTypeAndLength("affiliate", 2);
+ synthesizeAction("view");
+ checkFirstTypeAndLength("affiliate", 2);
+ synthesizeAction("view");
+ checkFirstTypeAndLength("affiliate", 2);
+ synthesizeAction("view");
+ checkFirstTypeAndLength("organic", 1);
+
+ // Cleanup.
+ NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+ DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+ gLinks.removeProvider(DirectoryLinksProvider);
+ DirectoryLinksProvider.removeObserver(gLinks);
+ Services.prefs.setCharPref(kPingUrlPref, kPingUrl);
+});
+
+add_task(function* test_frequencyCappedSites_click() {
+ Services.prefs.setCharPref(kPingUrlPref, "");
+ let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+ NewTabUtils.isTopPlacesSite = () => true;
+
+ let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+ DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
+
+ let testUrl = "http://frequency.capped/link";
+ let targets = ["top.site.com"];
+ let data = {
+ suggested: [{
+ type: "affiliate",
+ frecent_sites: targets,
+ url: testUrl,
+ adgroup_name: "Test"
+ }],
+ directory: [{
+ type: "organic",
+ url: "http://directory.site/"
+ }]
+ };
+ let dataURI = "data:application/json," + JSON.stringify(data);
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ // Wait for links to get loaded
+ let gLinks = NewTabUtils.links;
+ gLinks.addProvider(DirectoryLinksProvider);
+ gLinks.populateCache();
+ yield new Promise(resolve => {
+ NewTabUtils.allPages.register({
+ observe: _ => _,
+ update() {
+ NewTabUtils.allPages.unregister(this);
+ resolve();
+ }
+ });
+ });
+
+ function synthesizeAction(action) {
+ DirectoryLinksProvider.reportSitesAction([{
+ link: {
+ targetedSite: targets[0],
+ url: testUrl
+ }
+ }], action, 0);
+ }
+
+ function checkFirstTypeAndLength(type, length) {
+ let links = gLinks.getLinks();
+ do_check_eq(links[0].type, type);
+ do_check_eq(links.length, length);
+ }
+
+ // Make sure the link disappears after the first click
+ checkFirstTypeAndLength("affiliate", 2);
+ synthesizeAction("view");
+ checkFirstTypeAndLength("affiliate", 2);
+ synthesizeAction("click");
+ checkFirstTypeAndLength("organic", 1);
+
+ // Cleanup.
+ NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+ DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+ gLinks.removeProvider(DirectoryLinksProvider);
+ DirectoryLinksProvider.removeObserver(gLinks);
+ Services.prefs.setCharPref(kPingUrlPref, kPingUrl);
+});
+
+add_task(function* test_fetchAndCacheLinks_local() {
+ yield DirectoryLinksProvider.init();
+ yield cleanJsonFile();
+ // Trigger cache of data or chrome uri files in profD
+ yield DirectoryLinksProvider._fetchAndCacheLinks(kTestURL);
+ let data = yield readJsonFile();
+ isIdentical(data, kURLData);
+});
+
+add_task(function* test_fetchAndCacheLinks_remote() {
+ yield DirectoryLinksProvider.init();
+ yield cleanJsonFile();
+ // this must trigger directory links json download and save it to cache file
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, kExampleURL + "%LOCALE%");
+ do_check_eq(gLastRequestPath, kExamplePath + "en-US");
+ let data = yield readJsonFile();
+ isIdentical(data, kHttpHandlerData[kExamplePath]);
+});
+
+add_task(function* test_fetchAndCacheLinks_malformedURI() {
+ yield DirectoryLinksProvider.init();
+ yield cleanJsonFile();
+ let someJunk = "some junk";
+ try {
+ yield DirectoryLinksProvider._fetchAndCacheLinks(someJunk);
+ do_throw("Malformed URIs should fail")
+ } catch (e) {
+ do_check_eq(e, "Error fetching " + someJunk)
+ }
+
+ // File should be empty.
+ let data = yield readJsonFile();
+ isIdentical(data, "");
+});
+
+add_task(function* test_fetchAndCacheLinks_unknownHost() {
+ yield DirectoryLinksProvider.init();
+ yield cleanJsonFile();
+ let nonExistentServer = "http://localhost:56789/";
+ try {
+ yield DirectoryLinksProvider._fetchAndCacheLinks(nonExistentServer);
+ do_throw("BAD URIs should fail");
+ } catch (e) {
+ do_check_true(e.startsWith("Fetching " + nonExistentServer + " results in error code: "))
+ }
+
+ // File should be empty.
+ let data = yield readJsonFile();
+ isIdentical(data, "");
+});
+
+add_task(function* test_fetchAndCacheLinks_non200Status() {
+ yield DirectoryLinksProvider.init();
+ yield cleanJsonFile();
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, kFailURL);
+ do_check_eq(gLastRequestPath, kFailPath);
+ let data = yield readJsonFile();
+ isIdentical(data, {});
+});
+
+// To test onManyLinksChanged observer, trigger a fetch
+add_task(function* test_DirectoryLinksProvider__linkObservers() {
+ yield DirectoryLinksProvider.init();
+
+ let testObserver = new LinksChangeObserver();
+ DirectoryLinksProvider.addObserver(testObserver);
+ do_check_eq(DirectoryLinksProvider._observers.size, 1);
+ DirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
+
+ yield testObserver.deferred.promise;
+ DirectoryLinksProvider._removeObservers();
+ do_check_eq(DirectoryLinksProvider._observers.size, 0);
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider__prefObserver_url() {
+ yield promiseSetupDirectoryLinksProvider({linksURL: kTestURL});
+
+ let links = yield fetchData();
+ do_check_eq(links.length, 1);
+ let expectedData = [{url: "http://example.com", title: "LocalSource", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
+ isIdentical(links, expectedData);
+
+ // tests these 2 things:
+ // 1. _linksURL is properly set after the pref change
+ // 2. invalid source url is correctly handled
+ let exampleUrl = 'http://localhost:56789/bad';
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, exampleUrl);
+ do_check_eq(DirectoryLinksProvider._linksURL, exampleUrl);
+
+ // since the download fail, the directory file must remain the same
+ let newLinks = yield fetchData();
+ isIdentical(newLinks, expectedData);
+
+ // now remove the file, and re-download
+ yield cleanJsonFile();
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, exampleUrl + " ");
+ // we now should see empty links
+ newLinks = yield fetchData();
+ isIdentical(newLinks, []);
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_getLinks_noDirectoryData() {
+ let data = {
+ "directory": [],
+ };
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ let links = yield fetchData();
+ do_check_eq(links.length, 0);
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_getLinks_badData() {
+ let data = {
+ "en-US": {
+ "en-US": [{url: "http://example.com", title: "US"}],
+ },
+ };
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ // Make sure we get nothing for incorrectly formatted data
+ let links = yield fetchData();
+ do_check_eq(links.length, 0);
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function test_DirectoryLinksProvider_needsDownload() {
+ // test timestamping
+ DirectoryLinksProvider._lastDownloadMS = 0;
+ do_check_true(DirectoryLinksProvider._needsDownload);
+ DirectoryLinksProvider._lastDownloadMS = Date.now();
+ do_check_false(DirectoryLinksProvider._needsDownload);
+ DirectoryLinksProvider._lastDownloadMS = Date.now() - (60*60*24 + 1)*1000;
+ do_check_true(DirectoryLinksProvider._needsDownload);
+ DirectoryLinksProvider._lastDownloadMS = 0;
+});
+
+add_task(function* test_DirectoryLinksProvider_fetchAndCacheLinksIfNecessary() {
+ yield DirectoryLinksProvider.init();
+ yield cleanJsonFile();
+ // explicitly change source url to cause the download during setup
+ yield promiseSetupDirectoryLinksProvider({linksURL: kTestURL+" "});
+ yield DirectoryLinksProvider._fetchAndCacheLinksIfNecessary();
+
+ // inspect lastDownloadMS timestamp which should be 5 seconds less then now()
+ let lastDownloadMS = DirectoryLinksProvider._lastDownloadMS;
+ do_check_true((Date.now() - lastDownloadMS) < 5000);
+
+ // we should have fetched a new file during setup
+ let data = yield readJsonFile();
+ isIdentical(data, kURLData);
+
+ // attempt to download again - the timestamp should not change
+ yield DirectoryLinksProvider._fetchAndCacheLinksIfNecessary();
+ do_check_eq(DirectoryLinksProvider._lastDownloadMS, lastDownloadMS);
+
+ // clean the file and force the download
+ yield cleanJsonFile();
+ yield DirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
+ data = yield readJsonFile();
+ isIdentical(data, kURLData);
+
+ // make sure that failed download does not corrupt the file, nor changes lastDownloadMS
+ lastDownloadMS = DirectoryLinksProvider._lastDownloadMS;
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, "http://");
+ yield DirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
+ data = yield readJsonFile();
+ isIdentical(data, kURLData);
+ do_check_eq(DirectoryLinksProvider._lastDownloadMS, lastDownloadMS);
+
+ // _fetchAndCacheLinksIfNecessary must return same promise if download is in progress
+ let downloadPromise = DirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
+ let anotherPromise = DirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
+ do_check_true(downloadPromise === anotherPromise);
+ yield downloadPromise;
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_fetchDirectoryOnPrefChange() {
+ yield DirectoryLinksProvider.init();
+
+ let testObserver = new LinksChangeObserver();
+ DirectoryLinksProvider.addObserver(testObserver);
+
+ yield cleanJsonFile();
+ // ensure that provider does not think it needs to download
+ do_check_false(DirectoryLinksProvider._needsDownload);
+
+ // change the source URL, which should force directory download
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, kExampleURL);
+ // then wait for testObserver to fire and test that json is downloaded
+ yield testObserver.deferred.promise;
+ do_check_eq(gLastRequestPath, kExamplePath);
+ let data = yield readJsonFile();
+ isIdentical(data, kHttpHandlerData[kExamplePath]);
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_fetchDirectoryOnShow() {
+ yield promiseSetupDirectoryLinksProvider();
+
+ // set lastdownload to 0 to make DirectoryLinksProvider want to download
+ DirectoryLinksProvider._lastDownloadMS = 0;
+ do_check_true(DirectoryLinksProvider._needsDownload);
+
+ // download should happen on view
+ yield DirectoryLinksProvider.reportSitesAction([], "view");
+ do_check_true(DirectoryLinksProvider._lastDownloadMS != 0);
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_fetchDirectoryOnInit() {
+ // ensure preferences are set to defaults
+ yield promiseSetupDirectoryLinksProvider();
+ // now clean to provider, so we can init it again
+ yield promiseCleanDirectoryLinksProvider();
+
+ yield cleanJsonFile();
+ yield DirectoryLinksProvider.init();
+ let data = yield readJsonFile();
+ isIdentical(data, kURLData);
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_getLinksFromCorruptedFile() {
+ yield promiseSetupDirectoryLinksProvider();
+
+ // write bogus json to a file and attempt to fetch from it
+ let directoryLinksFilePath = OS.Path.join(OS.Constants.Path.profileDir, DIRECTORY_LINKS_FILE);
+ yield OS.File.writeAtomic(directoryLinksFilePath, '{"en-US":');
+ let data = yield fetchData();
+ isIdentical(data, []);
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_getAllowedLinks() {
+ let data = {"directory": [
+ {url: "ftp://example.com"},
+ {url: "http://example.net"},
+ {url: "javascript:5"},
+ {url: "https://example.com"},
+ {url: "httpJUNKjavascript:42"},
+ {url: "data:text/plain,hi"},
+ {url: "http/bork:eh"},
+ ]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ let links = yield fetchData();
+ do_check_eq(links.length, 2);
+
+ // The only remaining url should be http and https
+ do_check_eq(links[0].url, data["directory"][1].url);
+ do_check_eq(links[1].url, data["directory"][3].url);
+});
+
+add_task(function* test_DirectoryLinksProvider_getAllowedImages() {
+ let data = {"directory": [
+ {url: "http://example.com", imageURI: "ftp://example.com"},
+ {url: "http://example.com", imageURI: "http://example.net"},
+ {url: "http://example.com", imageURI: "javascript:5"},
+ {url: "http://example.com", imageURI: "https://example.com"},
+ {url: "http://example.com", imageURI: "httpJUNKjavascript:42"},
+ {url: "http://example.com", imageURI: "data:text/plain,hi"},
+ {url: "http://example.com", imageURI: "http/bork:eh"},
+ ]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ let links = yield fetchData();
+ do_check_eq(links.length, 2);
+
+ // The only remaining images should be https and data
+ do_check_eq(links[0].imageURI, data["directory"][3].imageURI);
+ do_check_eq(links[1].imageURI, data["directory"][5].imageURI);
+});
+
+add_task(function* test_DirectoryLinksProvider_getAllowedImages_base() {
+ let data = {"directory": [
+ {url: "http://example1.com", imageURI: "https://example.com"},
+ {url: "http://example2.com", imageURI: "https://tiles.cdn.mozilla.net"},
+ {url: "http://example3.com", imageURI: "https://tiles2.cdn.mozilla.net"},
+ {url: "http://example4.com", enhancedImageURI: "https://mozilla.net"},
+ {url: "http://example5.com", imageURI: "data:text/plain,hi"},
+ ]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ // Pretend we're using the default pref to trigger base matching
+ DirectoryLinksProvider.__linksURLModified = false;
+
+ let links = yield fetchData();
+ do_check_eq(links.length, 4);
+
+ // The only remaining images should be https with mozilla.net or data URI
+ do_check_eq(links[0].url, data["directory"][1].url);
+ do_check_eq(links[1].url, data["directory"][2].url);
+ do_check_eq(links[2].url, data["directory"][3].url);
+ do_check_eq(links[3].url, data["directory"][4].url);
+});
+
+add_task(function* test_DirectoryLinksProvider_getAllowedEnhancedImages() {
+ let data = {"directory": [
+ {url: "http://example.com", enhancedImageURI: "ftp://example.com"},
+ {url: "http://example.com", enhancedImageURI: "http://example.net"},
+ {url: "http://example.com", enhancedImageURI: "javascript:5"},
+ {url: "http://example.com", enhancedImageURI: "https://example.com"},
+ {url: "http://example.com", enhancedImageURI: "httpJUNKjavascript:42"},
+ {url: "http://example.com", enhancedImageURI: "data:text/plain,hi"},
+ {url: "http://example.com", enhancedImageURI: "http/bork:eh"},
+ ]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ let links = yield fetchData();
+ do_check_eq(links.length, 2);
+
+ // The only remaining enhancedImages should be http and https and data
+ do_check_eq(links[0].enhancedImageURI, data["directory"][3].enhancedImageURI);
+ do_check_eq(links[1].enhancedImageURI, data["directory"][5].enhancedImageURI);
+});
+
+add_task(function* test_DirectoryLinksProvider_getEnhancedLink() {
+ let data = {"enhanced": [
+ {url: "http://example.net", enhancedImageURI: "data:,net1"},
+ {url: "http://example.com", enhancedImageURI: "data:,com1"},
+ {url: "http://example.com", enhancedImageURI: "data:,com2"},
+ ]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ let links = yield fetchData();
+ do_check_eq(links.length, 0); // There are no directory links.
+
+ function checkEnhanced(url, image) {
+ let enhanced = DirectoryLinksProvider.getEnhancedLink({url: url});
+ do_check_eq(enhanced && enhanced.enhancedImageURI, image);
+ }
+
+ // Get the expected image for the same site
+ checkEnhanced("http://example.net/", "data:,net1");
+ checkEnhanced("http://example.net/path", "data:,net1");
+ checkEnhanced("https://www.example.net/", "data:,net1");
+ checkEnhanced("https://www3.example.net/", "data:,net1");
+
+ // Get the image of the last entry
+ checkEnhanced("http://example.com", "data:,com2");
+
+ // Get the inline enhanced image
+ let inline = DirectoryLinksProvider.getEnhancedLink({
+ url: "http://example.com/echo",
+ enhancedImageURI: "data:,echo",
+ });
+ do_check_eq(inline.enhancedImageURI, "data:,echo");
+ do_check_eq(inline.url, "http://example.com/echo");
+
+ // Undefined for not enhanced
+ checkEnhanced("http://sub.example.net/", undefined);
+ checkEnhanced("http://example.org", undefined);
+ checkEnhanced("http://localhost", undefined);
+ checkEnhanced("http://127.0.0.1", undefined);
+
+ // Make sure old data is not cached
+ data = {"enhanced": [
+ {url: "http://example.com", enhancedImageURI: "data:,fresh"},
+ ]};
+ dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+ links = yield fetchData();
+ do_check_eq(links.length, 0); // There are no directory links.
+ checkEnhanced("http://example.net", undefined);
+ checkEnhanced("http://example.com", "data:,fresh");
+});
+
+add_task(function* test_DirectoryLinksProvider_enhancedURIs() {
+ let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+ NewTabUtils.isTopPlacesSite = () => true;
+ let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+ DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
+
+ let data = {
+ "suggested": [
+ {url: "http://example.net", enhancedImageURI: "data:,net1", title:"SuggestedTitle", adgroup_name: "Test", frecent_sites: ["test.com"]}
+ ],
+ "directory": [
+ {url: "http://example.net", enhancedImageURI: "data:,net2", title:"DirectoryTitle"}
+ ]
+ };
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+
+ // Wait for links to get loaded
+ let gLinks = NewTabUtils.links;
+ gLinks.addProvider(DirectoryLinksProvider);
+ gLinks.populateCache();
+ yield new Promise(resolve => {
+ NewTabUtils.allPages.register({
+ observe: _ => _,
+ update() {
+ NewTabUtils.allPages.unregister(this);
+ resolve();
+ }
+ });
+ });
+
+ // Check that we've saved the directory tile.
+ let links = yield fetchData();
+ do_check_eq(links.length, 1);
+ do_check_eq(links[0].title, "DirectoryTitle");
+ do_check_eq(links[0].enhancedImageURI, "data:,net2");
+
+ // Check that the suggested tile with the same URL replaces the directory tile.
+ links = gLinks.getLinks();
+ do_check_eq(links.length, 1);
+ do_check_eq(links[0].title, "SuggestedTitle");
+ do_check_eq(links[0].enhancedImageURI, "data:,net1");
+
+ // Cleanup.
+ NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+ DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+ gLinks.removeProvider(DirectoryLinksProvider);
+});
+
+add_task(function test_DirectoryLinksProvider_setDefaultEnhanced() {
+ function checkDefault(expected) {
+ Services.prefs.clearUserPref(kNewtabEnhancedPref);
+ do_check_eq(Services.prefs.getBoolPref(kNewtabEnhancedPref), expected);
+ }
+
+ // Use the default donottrack prefs (enabled = false)
+ Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
+ checkDefault(true);
+
+ // Turn on DNT - no track
+ Services.prefs.setBoolPref("privacy.donottrackheader.enabled", true);
+ checkDefault(false);
+
+ // Turn off DNT header
+ Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
+ checkDefault(true);
+
+ // Clean up
+ Services.prefs.clearUserPref("privacy.donottrackheader.value");
+});
+
+add_task(function* test_timeSensetiveSuggestedTiles() {
+ // make tile json with start and end dates
+ let testStartTime = Date.now();
+ // start date is now + 1 seconds
+ let startDate = new Date(testStartTime + 1000);
+ // end date is now + 3 seconds
+ let endDate = new Date(testStartTime + 3000);
+ let suggestedTile = Object.assign({
+ time_limits: {
+ start: startDate.toISOString(),
+ end: endDate.toISOString(),
+ }
+ }, suggestedTile1);
+
+ // Initial setup
+ let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
+ let data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+
+ let testObserver = new TestTimingRun();
+ DirectoryLinksProvider.addObserver(testObserver);
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+ let links = yield fetchData();
+
+ let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+ NewTabUtils.isTopPlacesSite = function(site) {
+ return topSites.indexOf(site) >= 0;
+ }
+
+ let origGetProviderLinks = NewTabUtils.getProviderLinks;
+ NewTabUtils.getProviderLinks = function(provider) {
+ return links;
+ }
+
+ let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+ DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
+
+ do_check_eq(DirectoryLinksProvider._updateSuggestedTile(), undefined);
+
+ // this tester will fire twice: when start limit is reached and when tile link
+ // is removed upon end of the campaign, in which case deleteFlag will be set
+ function TestTimingRun() {
+ this.promise = new Promise(resolve => {
+ this.onLinkChanged = (directoryLinksProvider, link, ignoreFlag, deleteFlag) => {
+ // if we are not deleting, add link to links, so we can catch it's removal
+ if (!deleteFlag) {
+ links.unshift(link);
+ }
+
+ isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], ["hrblock.com", "1040.com"]);
+ do_check_eq(link.frecency, SUGGESTED_FRECENCY);
+ do_check_eq(link.type, "affiliate");
+ do_check_eq(link.url, suggestedTile.url);
+ let timeDelta = Date.now() - testStartTime;
+ if (!deleteFlag) {
+ // this is start timeout corresponding to campaign start
+ // a seconds must pass and targetedSite must be set
+ do_print("TESTING START timeDelta: " + timeDelta);
+ do_check_true(timeDelta >= 1000 / 2); // check for at least half time
+ do_check_eq(link.targetedSite, "hrblock.com");
+ do_check_true(DirectoryLinksProvider._campaignTimeoutID);
+ }
+ else {
+ // this is the campaign end timeout, so 3 seconds must pass
+ // and timeout should be cleared
+ do_print("TESTING END timeDelta: " + timeDelta);
+ do_check_true(timeDelta >= 3000 / 2); // check for at least half time
+ do_check_false(link.targetedSite);
+ do_check_false(DirectoryLinksProvider._campaignTimeoutID);
+ resolve();
+ }
+ };
+ });
+ }
+
+ // _updateSuggestedTile() is called when fetching directory links.
+ yield testObserver.promise;
+ DirectoryLinksProvider.removeObserver(testObserver);
+
+ // shoudl suggest nothing
+ do_check_eq(DirectoryLinksProvider._updateSuggestedTile(), undefined);
+
+ // set links back to contain directory tile only
+ links.shift();
+
+ // drop the end time - we should pick up the tile
+ suggestedTile.time_limits.end = null;
+ data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
+ dataURI = 'data:application/json,' + JSON.stringify(data);
+
+ // redownload json and getLinks to force time recomputation
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, dataURI);
+
+ // ensure that there's a link returned by _updateSuggestedTile and no timeout
+ let deferred = Promise.defer();
+ DirectoryLinksProvider.getLinks(() => {
+ let link = DirectoryLinksProvider._updateSuggestedTile();
+ // we should have a suggested tile and no timeout
+ do_check_eq(link.type, "affiliate");
+ do_check_eq(link.url, suggestedTile.url);
+ do_check_false(DirectoryLinksProvider._campaignTimeoutID);
+ deferred.resolve();
+ });
+ yield deferred.promise;
+
+ // repeat the test for end time only
+ suggestedTile.time_limits.start = null;
+ suggestedTile.time_limits.end = (new Date(Date.now() + 3000)).toISOString();
+
+ data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
+ dataURI = 'data:application/json,' + JSON.stringify(data);
+
+ // redownload json and call getLinks() to force time recomputation
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, dataURI);
+
+ // ensure that there's a link returned by _updateSuggestedTile and timeout set
+ deferred = Promise.defer();
+ DirectoryLinksProvider.getLinks(() => {
+ let link = DirectoryLinksProvider._updateSuggestedTile();
+ // we should have a suggested tile and timeout set
+ do_check_eq(link.type, "affiliate");
+ do_check_eq(link.url, suggestedTile.url);
+ do_check_true(DirectoryLinksProvider._campaignTimeoutID);
+ DirectoryLinksProvider._clearCampaignTimeout();
+ deferred.resolve();
+ });
+ yield deferred.promise;
+
+ // Cleanup
+ yield promiseCleanDirectoryLinksProvider();
+ NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+ NewTabUtils.getProviderLinks = origGetProviderLinks;
+ DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+});
+
+add_task(function test_setupStartEndTime() {
+ let currentTime = Date.now();
+ let dt = new Date(currentTime);
+ let link = {
+ time_limits: {
+ start: dt.toISOString()
+ }
+ };
+
+ // test ISO translation
+ DirectoryLinksProvider._setupStartEndTime(link);
+ do_check_eq(link.startTime, currentTime);
+
+ // test localtime translation
+ let shiftedDate = new Date(currentTime - dt.getTimezoneOffset()*60*1000);
+ link.time_limits.start = shiftedDate.toISOString().replace(/Z$/, "");
+
+ DirectoryLinksProvider._setupStartEndTime(link);
+ do_check_eq(link.startTime, currentTime);
+
+ // throw some garbage into date string
+ delete link.startTime;
+ link.time_limits.start = "no date"
+ DirectoryLinksProvider._setupStartEndTime(link);
+ do_check_false(link.startTime);
+
+ link.time_limits.start = "2015-99999-01T00:00:00"
+ DirectoryLinksProvider._setupStartEndTime(link);
+ do_check_false(link.startTime);
+
+ link.time_limits.start = "20150501T00:00:00"
+ DirectoryLinksProvider._setupStartEndTime(link);
+ do_check_false(link.startTime);
+});
+
+add_task(function* test_DirectoryLinksProvider_frequencyCapSetup() {
+ yield promiseSetupDirectoryLinksProvider();
+ yield DirectoryLinksProvider.init();
+
+ yield promiseCleanDirectoryLinksProvider();
+ yield DirectoryLinksProvider._readFrequencyCapFile();
+ isIdentical(DirectoryLinksProvider._frequencyCaps, {});
+
+ // setup few links
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "1",
+ });
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "2",
+ frequency_caps: {daily: 1, total: 2}
+ });
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "3",
+ frequency_caps: {total: 2}
+ });
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "4",
+ frequency_caps: {daily: 1}
+ });
+ let freqCapsObject = DirectoryLinksProvider._frequencyCaps;
+ let capObject = freqCapsObject["1"];
+ let defaultDaily = capObject.dailyCap;
+ let defaultTotal = capObject.totalCap;
+ // check if we have defaults set
+ do_check_true(capObject.dailyCap > 0);
+ do_check_true(capObject.totalCap > 0);
+ // check if defaults are properly handled
+ do_check_eq(freqCapsObject["2"].dailyCap, 1);
+ do_check_eq(freqCapsObject["2"].totalCap, 2);
+ do_check_eq(freqCapsObject["3"].dailyCap, defaultDaily);
+ do_check_eq(freqCapsObject["3"].totalCap, 2);
+ do_check_eq(freqCapsObject["4"].dailyCap, 1);
+ do_check_eq(freqCapsObject["4"].totalCap, defaultTotal);
+
+ // write object to file
+ yield DirectoryLinksProvider._writeFrequencyCapFile();
+ // empty out freqCapsObject and read file back
+ DirectoryLinksProvider._frequencyCaps = {};
+ yield DirectoryLinksProvider._readFrequencyCapFile();
+ // re-ran tests - they should all pass
+ do_check_eq(freqCapsObject["2"].dailyCap, 1);
+ do_check_eq(freqCapsObject["2"].totalCap, 2);
+ do_check_eq(freqCapsObject["3"].dailyCap, defaultDaily);
+ do_check_eq(freqCapsObject["3"].totalCap, 2);
+ do_check_eq(freqCapsObject["4"].dailyCap, 1);
+ do_check_eq(freqCapsObject["4"].totalCap, defaultTotal);
+
+ // wait a second and prune frequency caps
+ yield new Promise(resolve => {
+ setTimeout(resolve, 1100);
+ });
+
+ // update one link and create another
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "3",
+ frequency_caps: {daily: 1, total: 2}
+ });
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "7",
+ frequency_caps: {daily: 1, total: 2}
+ });
+ // now prune the ones that have been in the object longer than 1 second
+ DirectoryLinksProvider._pruneFrequencyCapUrls(1000);
+ // make sure all keys but "3" and "7" are deleted
+ Object.keys(DirectoryLinksProvider._frequencyCaps).forEach(key => {
+ do_check_true(key == "3" || key == "7");
+ });
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_getFrequencyCapLogic() {
+ yield promiseSetupDirectoryLinksProvider();
+ yield DirectoryLinksProvider.init();
+
+ // setup suggested links
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "1",
+ frequency_caps: {daily: 2, total: 4}
+ });
+
+ do_check_true(DirectoryLinksProvider._testFrequencyCapLimits("1"));
+ // exhaust daily views
+ DirectoryLinksProvider._addFrequencyCapView("1")
+ do_check_true(DirectoryLinksProvider._testFrequencyCapLimits("1"));
+ DirectoryLinksProvider._addFrequencyCapView("1")
+ do_check_false(DirectoryLinksProvider._testFrequencyCapLimits("1"));
+
+ // now step into the furture
+ let _wasTodayOrig = DirectoryLinksProvider._wasToday;
+ DirectoryLinksProvider._wasToday = function () { return false; }
+ // exhaust total views
+ DirectoryLinksProvider._addFrequencyCapView("1")
+ do_check_true(DirectoryLinksProvider._testFrequencyCapLimits("1"));
+ DirectoryLinksProvider._addFrequencyCapView("1")
+ // reached totalViews 4, should return false
+ do_check_false(DirectoryLinksProvider._testFrequencyCapLimits("1"));
+
+ // add more views by updating configuration
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "1",
+ frequency_caps: {daily: 5, total: 10}
+ });
+ // should be true, since we have more total views
+ do_check_true(DirectoryLinksProvider._testFrequencyCapLimits("1"));
+
+ // set click flag
+ DirectoryLinksProvider._setFrequencyCapClick("1");
+ // always false after click
+ do_check_false(DirectoryLinksProvider._testFrequencyCapLimits("1"));
+
+ // use unknown urls and ensure nothing breaks
+ DirectoryLinksProvider._addFrequencyCapView("nosuch.url");
+ DirectoryLinksProvider._setFrequencyCapClick("nosuch.url");
+ // testing unknown url should always return false
+ do_check_false(DirectoryLinksProvider._testFrequencyCapLimits("nosuch.url"));
+
+ // reset _wasToday back to original function
+ DirectoryLinksProvider._wasToday = _wasTodayOrig;
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_getFrequencyCapReportSiteAction() {
+ yield promiseSetupDirectoryLinksProvider();
+ yield DirectoryLinksProvider.init();
+
+ // setup suggested links
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "bar.com",
+ frequency_caps: {daily: 2, total: 4}
+ });
+
+ do_check_true(DirectoryLinksProvider._testFrequencyCapLimits("bar.com"));
+ // report site action
+ yield DirectoryLinksProvider.reportSitesAction([{
+ link: {
+ targetedSite: "foo.com",
+ url: "bar.com"
+ },
+ isPinned: function() { return false; },
+ }], "view", 0);
+
+ // read file content and ensure that view counters are updated
+ let data = yield readJsonFile(DirectoryLinksProvider._frequencyCapFilePath);
+ do_check_eq(data["bar.com"].dailyViews, 1);
+ do_check_eq(data["bar.com"].totalViews, 1);
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_DirectoryLinksProvider_ClickRemoval() {
+ yield promiseSetupDirectoryLinksProvider();
+ yield DirectoryLinksProvider.init();
+ let landingUrl = "http://foo.com";
+
+ // setup suggested links
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: landingUrl,
+ frequency_caps: {daily: 2, total: 4}
+ });
+
+ // add views
+ DirectoryLinksProvider._addFrequencyCapView(landingUrl)
+ DirectoryLinksProvider._addFrequencyCapView(landingUrl)
+ // make a click
+ DirectoryLinksProvider._setFrequencyCapClick(landingUrl);
+
+ // views must be 2 and click must be set
+ do_check_eq(DirectoryLinksProvider._frequencyCaps[landingUrl].totalViews, 2);
+ do_check_true(DirectoryLinksProvider._frequencyCaps[landingUrl].clicked);
+
+ // now insert a visit into places
+ yield new Promise(resolve => {
+ PlacesUtils.asyncHistory.updatePlaces(
+ {
+ uri: NetUtil.newURI(landingUrl),
+ title: "HELLO",
+ visits: [{
+ visitDate: Date.now()*1000,
+ transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
+ }]
+ },
+ {
+ handleError: function () { do_check_true(false); },
+ handleResult: function () {},
+ handleCompletion: function () { resolve(); }
+ }
+ );
+ });
+
+ function UrlDeletionTester() {
+ this.promise = new Promise(resolve => {
+ this.onDeleteURI = (directoryLinksProvider, link) => {
+ resolve();
+ };
+ this.onClearHistory = (directoryLinksProvider) => {
+ resolve();
+ };
+ });
+ }
+
+ let testObserver = new UrlDeletionTester();
+ DirectoryLinksProvider.addObserver(testObserver);
+
+ PlacesUtils.bhistory.removePage(NetUtil.newURI(landingUrl));
+ yield testObserver.promise;
+ DirectoryLinksProvider.removeObserver(testObserver);
+ // views must be 2 and click should not exist
+ do_check_eq(DirectoryLinksProvider._frequencyCaps[landingUrl].totalViews, 2);
+ do_check_false(DirectoryLinksProvider._frequencyCaps[landingUrl].hasOwnProperty("clicked"));
+
+ // verify that disk written data is kosher
+ let data = yield readJsonFile(DirectoryLinksProvider._frequencyCapFilePath);
+ do_check_eq(data[landingUrl].totalViews, 2);
+ do_check_false(data[landingUrl].hasOwnProperty("clicked"));
+
+ // now test clear history
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: landingUrl,
+ frequency_caps: {daily: 2, total: 4}
+ });
+ DirectoryLinksProvider._updateFrequencyCapSettings({
+ url: "http://bar.com",
+ frequency_caps: {daily: 2, total: 4}
+ });
+
+ DirectoryLinksProvider._setFrequencyCapClick(landingUrl);
+ DirectoryLinksProvider._setFrequencyCapClick("http://bar.com");
+ // both tiles must have clicked
+ do_check_true(DirectoryLinksProvider._frequencyCaps[landingUrl].clicked);
+ do_check_true(DirectoryLinksProvider._frequencyCaps["http://bar.com"].clicked);
+
+ testObserver = new UrlDeletionTester();
+ DirectoryLinksProvider.addObserver(testObserver);
+ yield PlacesTestUtils.clearHistory();
+
+ yield testObserver.promise;
+ DirectoryLinksProvider.removeObserver(testObserver);
+ // no clicks should remain in the cap object
+ do_check_false(DirectoryLinksProvider._frequencyCaps[landingUrl].hasOwnProperty("clicked"));
+ do_check_false(DirectoryLinksProvider._frequencyCaps["http://bar.com"].hasOwnProperty("clicked"));
+
+ // verify that disk written data is kosher
+ data = yield readJsonFile(DirectoryLinksProvider._frequencyCapFilePath);
+ do_check_false(data[landingUrl].hasOwnProperty("clicked"));
+ do_check_false(data["http://bar.com"].hasOwnProperty("clicked"));
+
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function test_DirectoryLinksProvider_anonymous() {
+ do_check_true(DirectoryLinksProvider._newXHR().mozAnon);
+});
+
+add_task(function* test_sanitizeExplanation() {
+ // Note: this is a basic test to ensure we applied sanitization to the link explanation.
+ // Full testing for appropriate sanitization is done in parser/xml/test/unit/test_sanitizer.js.
+ let data = {"suggested": [suggestedTile5]};
+ let dataURI = 'data:application/json,' + encodeURIComponent(JSON.stringify(data));
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+ yield fetchData();
+
+ let suggestedSites = [...DirectoryLinksProvider._suggestedLinks.keys()];
+ do_check_eq(suggestedSites.indexOf("eviltarget.com"), 0);
+ do_check_eq(suggestedSites.length, 1);
+
+ let suggestedLink = [...DirectoryLinksProvider._suggestedLinks.get(suggestedSites[0]).values()][0];
+ do_check_eq(suggestedLink.explanation, "This is an evil tile X muhahaha");
+ do_check_eq(suggestedLink.targetedName, "WE ARE EVIL ");
+});
+
+add_task(function* test_inadjecentSites() {
+ let suggestedTile = Object.assign({
+ check_inadjacency: true
+ }, suggestedTile1);
+
+ // Initial setup
+ let topSites = ["1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
+ let data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+
+ let testObserver = new TestFirstRun();
+ DirectoryLinksProvider.addObserver(testObserver);
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+ let links = yield fetchData();
+
+ let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+ NewTabUtils.isTopPlacesSite = function(site) {
+ return topSites.indexOf(site) >= 0;
+ }
+
+ let origGetProviderLinks = NewTabUtils.getProviderLinks;
+ NewTabUtils.getProviderLinks = function(provider) {
+ return links;
+ }
+
+ let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+ DirectoryLinksProvider._getCurrentTopSiteCount = () => {
+ origCurrentTopSiteCount.apply(DirectoryLinksProvider);
+ return 8;
+ };
+
+ // store oroginal inadjacent sites url
+ let origInadjacentSitesUrl = DirectoryLinksProvider._inadjacentSitesUrl;
+
+ // loading inadjacent sites list function
+ function setInadjacentSites(sites) {
+ let badSiteB64 = [];
+ sites.forEach(site => {
+ badSiteB64.push(DirectoryLinksProvider._generateHash(site));
+ });
+ let theList = {"domains": badSiteB64};
+ let uri = 'data:application/json,' + JSON.stringify(theList);
+ DirectoryLinksProvider._inadjacentSitesUrl = uri;
+ return DirectoryLinksProvider._loadInadjacentSites();
+ }
+
+ // setup gLinks loader
+ let gLinks = NewTabUtils.links;
+ gLinks.addProvider(DirectoryLinksProvider);
+
+ function updateNewTabCache() {
+ gLinks.populateCache();
+ return new Promise(resolve => {
+ NewTabUtils.allPages.register({
+ observe: _ => _,
+ update() {
+ NewTabUtils.allPages.unregister(this);
+ resolve();
+ }
+ });
+ });
+ }
+
+ // no suggested file
+ do_check_eq(DirectoryLinksProvider._updateSuggestedTile(), undefined);
+ // _avoidInadjacentSites should be set, since link.check_inadjacency is on
+ do_check_true(DirectoryLinksProvider._avoidInadjacentSites);
+ // make sure example.com is included in inadjacent sites list
+ do_check_true(DirectoryLinksProvider._isInadjacentLink({baseDomain: "example.com"}));
+
+ function TestFirstRun() {
+ this.promise = new Promise(resolve => {
+ this.onLinkChanged = (directoryLinksProvider, link) => {
+ do_check_eq(link.url, suggestedTile.url);
+ do_check_eq(link.type, "affiliate");
+ resolve();
+ };
+ });
+ }
+
+ // Test first call to '_updateSuggestedTile()', called when fetching directory links.
+ yield testObserver.promise;
+ DirectoryLinksProvider.removeObserver(testObserver);
+
+ // update newtab cache
+ yield updateNewTabCache();
+ // this should have set
+ do_check_true(DirectoryLinksProvider._avoidInadjacentSites);
+
+ // there should be siggested link
+ let link = DirectoryLinksProvider._updateSuggestedTile();
+ do_check_eq(link.url, "http://turbotax.com");
+ // and it should have avoidInadjacentSites flag
+ do_check_true(link.check_inadjacency);
+
+ // make someothersite.com inadjacent
+ yield setInadjacentSites(["someothersite.com"]);
+
+ // there should be no suggested link
+ link = DirectoryLinksProvider._updateSuggestedTile();
+ do_check_false(link);
+ do_check_true(DirectoryLinksProvider._newTabHasInadjacentSite);
+
+ // _handleLinkChanged must return true on inadjacent site
+ do_check_true(DirectoryLinksProvider._handleLinkChanged({
+ url: "http://someothersite.com",
+ type: "history",
+ }));
+ // _handleLinkChanged must return false on ok site
+ do_check_false(DirectoryLinksProvider._handleLinkChanged({
+ url: "http://foobar.com",
+ type: "history",
+ }));
+
+ // change inadjacent list to sites not on newtab page
+ yield setInadjacentSites(["foo.com", "bar.com"]);
+
+ link = DirectoryLinksProvider._updateSuggestedTile();
+ // we should now have a link
+ do_check_true(link);
+ do_check_eq(link.url, "http://turbotax.com");
+
+ // make newtab offending again
+ yield setInadjacentSites(["someothersite.com", "foo.com"]);
+ // there should be no suggested link
+ link = DirectoryLinksProvider._updateSuggestedTile();
+ do_check_false(link);
+ do_check_true(DirectoryLinksProvider._newTabHasInadjacentSite);
+
+ // remove avoidInadjacentSites flag from suggested tile and reload json
+ delete suggestedTile.check_inadjacency;
+ data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
+ dataURI = 'data:application/json,' + JSON.stringify(data);
+ yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, dataURI);
+ yield fetchData();
+
+ // inadjacent checking should be disabled
+ do_check_false(DirectoryLinksProvider._avoidInadjacentSites);
+ link = DirectoryLinksProvider._updateSuggestedTile();
+ do_check_true(link);
+ do_check_eq(link.url, "http://turbotax.com");
+ do_check_false(DirectoryLinksProvider._newTabHasInadjacentSite);
+
+ // _handleLinkChanged should return false now, even if newtab has bad site
+ do_check_false(DirectoryLinksProvider._handleLinkChanged({
+ url: "http://someothersite.com",
+ type: "history",
+ }));
+
+ // test _isInadjacentLink
+ do_check_true(DirectoryLinksProvider._isInadjacentLink({baseDomain: "someothersite.com"}));
+ do_check_false(DirectoryLinksProvider._isInadjacentLink({baseDomain: "bar.com"}));
+ do_check_true(DirectoryLinksProvider._isInadjacentLink({url: "http://www.someothersite.com"}));
+ do_check_false(DirectoryLinksProvider._isInadjacentLink({url: "http://www.bar.com"}));
+ // try to crash _isInadjacentLink
+ do_check_false(DirectoryLinksProvider._isInadjacentLink({baseDomain: ""}));
+ do_check_false(DirectoryLinksProvider._isInadjacentLink({url: ""}));
+ do_check_false(DirectoryLinksProvider._isInadjacentLink({url: "http://localhost:8081/"}));
+ do_check_false(DirectoryLinksProvider._isInadjacentLink({url: "abracodabra"}));
+ do_check_false(DirectoryLinksProvider._isInadjacentLink({}));
+
+ // test _checkForInadjacentSites
+ do_check_true(DirectoryLinksProvider._checkForInadjacentSites());
+
+ // Cleanup
+ gLinks.removeProvider(DirectoryLinksProvider);
+ DirectoryLinksProvider._inadjacentSitesUrl = origInadjacentSitesUrl;
+ NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+ NewTabUtils.getProviderLinks = origGetProviderLinks;
+ DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+ yield promiseCleanDirectoryLinksProvider();
+});
+
+add_task(function* test_blockSuggestedTiles() {
+ // Initial setup
+ let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
+ let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]};
+ let dataURI = 'data:application/json,' + JSON.stringify(data);
+
+ yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+ let links = yield fetchData();
+
+ let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+ NewTabUtils.isTopPlacesSite = function(site) {
+ return topSites.indexOf(site) >= 0;
+ }
+
+ let origGetProviderLinks = NewTabUtils.getProviderLinks;
+ NewTabUtils.getProviderLinks = function(provider) {
+ return links;
+ }
+
+ let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+ DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
+
+ // load the links
+ yield new Promise(resolve => {
+ DirectoryLinksProvider.getLinks(resolve);
+ });
+
+ // ensure that tile is suggested
+ let suggestedLink = DirectoryLinksProvider._updateSuggestedTile();
+ do_check_true(suggestedLink.frecent_sites);
+
+ // block suggested tile in a regular way
+ DirectoryLinksProvider.reportSitesAction([{
+ isPinned: function() { return false; },
+ link: Object.assign({frecency: 1000}, suggestedLink)
+ }], "block", 0);
+
+ // suggested tile still must be recommended
+ suggestedLink = DirectoryLinksProvider._updateSuggestedTile();
+ do_check_true(suggestedLink.frecent_sites);
+
+ // timestamp suggested_block in the frequency cap object
+ DirectoryLinksProvider.handleSuggestedTileBlock();
+ // no more recommendations should be seen
+ do_check_eq(DirectoryLinksProvider._updateSuggestedTile(), undefined);
+
+ // move lastUpdated for suggested tile into the past
+ DirectoryLinksProvider._frequencyCaps["ignore://suggested_block"].lastUpdated = Date.now() - 25*60*60*1000;
+ // ensure that suggested tile updates again
+ suggestedLink = DirectoryLinksProvider._updateSuggestedTile();
+ do_check_true(suggestedLink.frecent_sites);
+
+ // Cleanup
+ yield promiseCleanDirectoryLinksProvider();
+ NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+ NewTabUtils.getProviderLinks = origGetProviderLinks;
+ DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+});
diff --git a/browser/modules/test/xpcshell/test_LaterRun.js b/browser/modules/test/xpcshell/test_LaterRun.js
new file mode 100644
index 000000000..7b45c7cd5
--- /dev/null
+++ b/browser/modules/test/xpcshell/test_LaterRun.js
@@ -0,0 +1,138 @@
+"use strict";
+
+const kEnabledPref = "browser.laterrun.enabled";
+const kPagePrefRoot = "browser.laterrun.pages.";
+const kSessionCountPref = "browser.laterrun.bookkeeping.sessionCount";
+const kProfileCreationTime = "browser.laterrun.bookkeeping.profileCreationTime";
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource:///modules/LaterRun.jsm");
+
+Services.prefs.setBoolPref(kEnabledPref, true);
+Components.utils.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
+add_task(function* test_page_applies() {
+ Services.prefs.setCharPref(kPagePrefRoot + "test_LaterRun_unittest.url", "https://www.mozilla.org/%VENDOR%/%NAME%/%ID%/%VERSION%/");
+ Services.prefs.setIntPref(kPagePrefRoot + "test_LaterRun_unittest.minimumHoursSinceInstall", 10);
+ Services.prefs.setIntPref(kPagePrefRoot + "test_LaterRun_unittest.minimumSessionCount", 3);
+
+ let pages = LaterRun.readPages();
+ // We have to filter the pages because it's possible Firefox ships with other URLs
+ // that get included in this test.
+ pages = pages.filter(page => page.pref == kPagePrefRoot + "test_LaterRun_unittest.");
+ Assert.equal(pages.length, 1, "Got 1 page");
+ let page = pages[0];
+ Assert.equal(page.pref, kPagePrefRoot + "test_LaterRun_unittest.", "Should know its own pref");
+ Assert.equal(page.minimumHoursSinceInstall, 10, "Needs to have 10 hours since install");
+ Assert.equal(page.minimumSessionCount, 3, "Needs to have 3 sessions");
+ Assert.equal(page.requireBoth, false, "Either requirement is enough");
+ let expectedURL = "https://www.mozilla.org/" +
+ Services.appinfo.vendor + "/" +
+ Services.appinfo.name + "/" +
+ Services.appinfo.ID + "/" +
+ Services.appinfo.version + "/";
+ Assert.equal(page.url, expectedURL, "URL is stored correctly");
+
+ Assert.ok(page.applies({hoursSinceInstall: 1, sessionCount: 3}),
+ "Applies when session count has been met.");
+ Assert.ok(page.applies({hoursSinceInstall: 1, sessionCount: 4}),
+ "Applies when session count has been exceeded.");
+ Assert.ok(page.applies({hoursSinceInstall: 10, sessionCount: 2}),
+ "Applies when total session time has been met.");
+ Assert.ok(page.applies({hoursSinceInstall: 20, sessionCount: 2}),
+ "Applies when total session time has been exceeded.");
+ Assert.ok(page.applies({hoursSinceInstall: 10, sessionCount: 3}),
+ "Applies when both time and session count have been met.");
+ Assert.ok(!page.applies({hoursSinceInstall: 1, sessionCount: 1}),
+ "Does not apply when neither time and session count have been met.");
+
+ page.requireBoth = true;
+
+ Assert.ok(!page.applies({hoursSinceInstall: 1, sessionCount: 3}),
+ "Does not apply when only session count has been met.");
+ Assert.ok(!page.applies({hoursSinceInstall: 1, sessionCount: 4}),
+ "Does not apply when only session count has been exceeded.");
+ Assert.ok(!page.applies({hoursSinceInstall: 10, sessionCount: 2}),
+ "Does not apply when only total session time has been met.");
+ Assert.ok(!page.applies({hoursSinceInstall: 20, sessionCount: 2}),
+ "Does not apply when only total session time has been exceeded.");
+ Assert.ok(page.applies({hoursSinceInstall: 10, sessionCount: 3}),
+ "Applies when both time and session count have been met.");
+ Assert.ok(!page.applies({hoursSinceInstall: 1, sessionCount: 1}),
+ "Does not apply when neither time and session count have been met.");
+
+ // Check that pages that have run never apply:
+ Services.prefs.setBoolPref(kPagePrefRoot + "test_LaterRun_unittest.hasRun", true);
+ page.requireBoth = false;
+
+ Assert.ok(!page.applies({hoursSinceInstall: 1, sessionCount: 3}),
+ "Does not apply when page has already run (sessionCount equal).");
+ Assert.ok(!page.applies({hoursSinceInstall: 1, sessionCount: 4}),
+ "Does not apply when page has already run (sessionCount exceeding).");
+ Assert.ok(!page.applies({hoursSinceInstall: 10, sessionCount: 2}),
+ "Does not apply when page has already run (hoursSinceInstall equal).");
+ Assert.ok(!page.applies({hoursSinceInstall: 20, sessionCount: 2}),
+ "Does not apply when page has already run (hoursSinceInstall exceeding).");
+ Assert.ok(!page.applies({hoursSinceInstall: 10, sessionCount: 3}),
+ "Does not apply when page has already run (both criteria equal).");
+ Assert.ok(!page.applies({hoursSinceInstall: 1, sessionCount: 1}),
+ "Does not apply when page has already run (both criteria insufficient anyway).");
+
+ clearAllPagePrefs();
+});
+
+add_task(function* test_get_URL() {
+ Services.prefs.setIntPref(kProfileCreationTime, Math.floor((Date.now() - 11 * 60 * 60 * 1000) / 1000));
+ Services.prefs.setCharPref(kPagePrefRoot + "test_LaterRun_unittest.url", "https://www.mozilla.org/");
+ Services.prefs.setIntPref(kPagePrefRoot + "test_LaterRun_unittest.minimumHoursSinceInstall", 10);
+ Services.prefs.setIntPref(kPagePrefRoot + "test_LaterRun_unittest.minimumSessionCount", 3);
+ let pages = LaterRun.readPages();
+ // We have to filter the pages because it's possible Firefox ships with other URLs
+ // that get included in this test.
+ pages = pages.filter(page => page.pref == kPagePrefRoot + "test_LaterRun_unittest.");
+ Assert.equal(pages.length, 1, "Should only be 1 matching page");
+ let page = pages[0];
+ let url;
+ do {
+ url = LaterRun.getURL();
+ // We have to loop because it's possible Firefox ships with other URLs that get triggered by
+ // this test.
+ } while (url && url != "https://www.mozilla.org/");
+ Assert.equal(url, "https://www.mozilla.org/", "URL should be as expected when prefs are set.");
+ Assert.ok(Services.prefs.prefHasUserValue(kPagePrefRoot + "test_LaterRun_unittest.hasRun"), "Should have set pref");
+ Assert.ok(Services.prefs.getBoolPref(kPagePrefRoot + "test_LaterRun_unittest.hasRun"), "Should have set pref to true");
+ Assert.ok(page.hasRun, "Other page objects should know it has run, too.");
+
+ clearAllPagePrefs();
+});
+
+add_task(function* test_insecure_urls() {
+ Services.prefs.setCharPref(kPagePrefRoot + "test_LaterRun_unittest.url", "http://www.mozilla.org/");
+ Services.prefs.setIntPref(kPagePrefRoot + "test_LaterRun_unittest.minimumHoursSinceInstall", 10);
+ Services.prefs.setIntPref(kPagePrefRoot + "test_LaterRun_unittest.minimumSessionCount", 3);
+ let pages = LaterRun.readPages();
+ // We have to filter the pages because it's possible Firefox ships with other URLs
+ // that get triggered in this test.
+ pages = pages.filter(page => page.pref == kPagePrefRoot + "test_LaterRun_unittest.");
+ Assert.equal(pages.length, 0, "URL with non-https scheme should get ignored");
+ clearAllPagePrefs();
+});
+
+add_task(function* test_dynamic_pref_getter_setter() {
+ delete LaterRun._sessionCount;
+ Services.prefs.setIntPref(kSessionCountPref, 0);
+ Assert.equal(LaterRun.sessionCount, 0, "Should start at 0");
+
+ LaterRun.sessionCount++;
+ Assert.equal(LaterRun.sessionCount, 1, "Should increment.");
+ Assert.equal(Services.prefs.getIntPref(kSessionCountPref), 1, "Should update pref");
+});
+
+function clearAllPagePrefs() {
+ let allChangedPrefs = Services.prefs.getChildList(kPagePrefRoot);
+ for (let pref of allChangedPrefs) {
+ Services.prefs.clearUserPref(pref);
+ }
+}
+
diff --git a/browser/modules/test/xpcshell/test_SitePermissions.js b/browser/modules/test/xpcshell/test_SitePermissions.js
new file mode 100644
index 000000000..808d96599
--- /dev/null
+++ b/browser/modules/test/xpcshell/test_SitePermissions.js
@@ -0,0 +1,115 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+"use strict";
+
+Components.utils.import("resource:///modules/SitePermissions.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+add_task(function* testPermissionsListing() {
+ Assert.deepEqual(SitePermissions.listPermissions().sort(),
+ ["camera", "cookie", "desktop-notification", "geo", "image",
+ "indexedDB", "install", "microphone", "popup", "screen"],
+ "Correct list of all permissions");
+});
+
+add_task(function* testGetAllByURI() {
+ // check that it returns an empty array on an invalid URI
+ // like a file URI, which doesn't support site permissions
+ let wrongURI = Services.io.newURI("file:///example.js", null, null)
+ Assert.deepEqual(SitePermissions.getAllByURI(wrongURI), []);
+
+ let uri = Services.io.newURI("https://example.com", null, null)
+ Assert.deepEqual(SitePermissions.getAllByURI(uri), []);
+
+ SitePermissions.set(uri, "camera", SitePermissions.ALLOW);
+ Assert.deepEqual(SitePermissions.getAllByURI(uri), [
+ { id: "camera", state: SitePermissions.ALLOW }
+ ]);
+
+ SitePermissions.set(uri, "microphone", SitePermissions.SESSION);
+ SitePermissions.set(uri, "desktop-notification", SitePermissions.BLOCK);
+
+ Assert.deepEqual(SitePermissions.getAllByURI(uri), [
+ { id: "camera", state: SitePermissions.ALLOW },
+ { id: "microphone", state: SitePermissions.SESSION },
+ { id: "desktop-notification", state: SitePermissions.BLOCK }
+ ]);
+
+ SitePermissions.remove(uri, "microphone");
+ Assert.deepEqual(SitePermissions.getAllByURI(uri), [
+ { id: "camera", state: SitePermissions.ALLOW },
+ { id: "desktop-notification", state: SitePermissions.BLOCK }
+ ]);
+
+ SitePermissions.remove(uri, "camera");
+ SitePermissions.remove(uri, "desktop-notification");
+ Assert.deepEqual(SitePermissions.getAllByURI(uri), []);
+
+ // XXX Bug 1303108 - Control Center should only show non-default permissions
+ SitePermissions.set(uri, "addon", SitePermissions.BLOCK);
+ Assert.deepEqual(SitePermissions.getAllByURI(uri), []);
+ SitePermissions.remove(uri, "addon");
+});
+
+add_task(function* testGetPermissionDetailsByURI() {
+ // check that it returns an empty array on an invalid URI
+ // like a file URI, which doesn't support site permissions
+ let wrongURI = Services.io.newURI("file:///example.js", null, null)
+ Assert.deepEqual(SitePermissions.getPermissionDetailsByURI(wrongURI), []);
+
+ let uri = Services.io.newURI("https://example.com", null, null)
+
+ SitePermissions.set(uri, "camera", SitePermissions.ALLOW);
+ SitePermissions.set(uri, "cookie", SitePermissions.SESSION);
+ SitePermissions.set(uri, "popup", SitePermissions.BLOCK);
+
+ let permissions = SitePermissions.getPermissionDetailsByURI(uri);
+
+ let camera = permissions.find(({id}) => id === "camera");
+ Assert.deepEqual(camera, {
+ id: "camera",
+ label: "Use the Camera",
+ state: SitePermissions.ALLOW,
+ availableStates: [
+ { id: SitePermissions.UNKNOWN, label: "Always Ask" },
+ { id: SitePermissions.ALLOW, label: "Allow" },
+ { id: SitePermissions.BLOCK, label: "Block" },
+ ]
+ });
+
+ // check that removed permissions (State.UNKNOWN) are skipped
+ SitePermissions.remove(uri, "camera");
+ permissions = SitePermissions.getPermissionDetailsByURI(uri);
+
+ camera = permissions.find(({id}) => id === "camera");
+ Assert.equal(camera, undefined);
+
+ // check that different available state values are represented
+
+ let cookie = permissions.find(({id}) => id === "cookie");
+ Assert.deepEqual(cookie, {
+ id: "cookie",
+ label: "Set Cookies",
+ state: SitePermissions.SESSION,
+ availableStates: [
+ { id: SitePermissions.ALLOW, label: "Allow" },
+ { id: SitePermissions.SESSION, label: "Allow for Session" },
+ { id: SitePermissions.BLOCK, label: "Block" },
+ ]
+ });
+
+ let popup = permissions.find(({id}) => id === "popup");
+ Assert.deepEqual(popup, {
+ id: "popup",
+ label: "Open Pop-up Windows",
+ state: SitePermissions.BLOCK,
+ availableStates: [
+ { id: SitePermissions.ALLOW, label: "Allow" },
+ { id: SitePermissions.BLOCK, label: "Block" },
+ ]
+ });
+
+ SitePermissions.remove(uri, "cookie");
+ SitePermissions.remove(uri, "popup");
+});
diff --git a/browser/modules/test/xpcshell/xpcshell.ini b/browser/modules/test/xpcshell/xpcshell.ini
new file mode 100644
index 000000000..28df9c4ed
--- /dev/null
+++ b/browser/modules/test/xpcshell/xpcshell.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+head =
+tail =
+firefox-appdir = browser
+skip-if = toolkit == 'android'
+
+[test_AttributionCode.js]
+skip-if = os != 'win'
+[test_DirectoryLinksProvider.js]
+[test_SitePermissions.js]
+[test_LaterRun.js]