summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/tests/favicons
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/places/tests/favicons')
-rw-r--r--toolkit/components/places/tests/favicons/.eslintrc.js7
-rw-r--r--toolkit/components/places/tests/favicons/expected-favicon-big32.jpg.pngbin0 -> 3105 bytes
-rw-r--r--toolkit/components/places/tests/favicons/expected-favicon-big4.jpg.pngbin0 -> 563 bytes
-rw-r--r--toolkit/components/places/tests/favicons/expected-favicon-big48.ico.pngbin0 -> 1425 bytes
-rw-r--r--toolkit/components/places/tests/favicons/expected-favicon-big64.png.pngbin0 -> 3157 bytes
-rw-r--r--toolkit/components/places/tests/favicons/expected-favicon-scale160x3.jpg.pngbin0 -> 175 bytes
-rw-r--r--toolkit/components/places/tests/favicons/expected-favicon-scale3x160.jpg.pngbin0 -> 169 bytes
-rw-r--r--toolkit/components/places/tests/favicons/favicon-big16.icobin0 -> 1406 bytes
-rw-r--r--toolkit/components/places/tests/favicons/favicon-big32.jpgbin0 -> 3494 bytes
-rw-r--r--toolkit/components/places/tests/favicons/favicon-big4.jpgbin0 -> 4751 bytes
-rw-r--r--toolkit/components/places/tests/favicons/favicon-big48.icobin0 -> 56646 bytes
-rw-r--r--toolkit/components/places/tests/favicons/favicon-big64.pngbin0 -> 10698 bytes
-rw-r--r--toolkit/components/places/tests/favicons/favicon-normal16.pngbin0 -> 286 bytes
-rw-r--r--toolkit/components/places/tests/favicons/favicon-normal32.pngbin0 -> 344 bytes
-rw-r--r--toolkit/components/places/tests/favicons/favicon-scale160x3.jpgbin0 -> 5095 bytes
-rw-r--r--toolkit/components/places/tests/favicons/favicon-scale3x160.jpgbin0 -> 5059 bytes
-rw-r--r--toolkit/components/places/tests/favicons/head_favicons.js105
-rw-r--r--toolkit/components/places/tests/favicons/test_expireAllFavicons.js39
-rw-r--r--toolkit/components/places/tests/favicons/test_favicons_conversions.js131
-rw-r--r--toolkit/components/places/tests/favicons/test_getFaviconDataForPage.js57
-rw-r--r--toolkit/components/places/tests/favicons/test_getFaviconURLForPage.js51
-rw-r--r--toolkit/components/places/tests/favicons/test_moz-anno_favicon_mime_type.js90
-rw-r--r--toolkit/components/places/tests/favicons/test_page-icon_protocol.js66
-rw-r--r--toolkit/components/places/tests/favicons/test_query_result_favicon_changed_on_child.js74
-rw-r--r--toolkit/components/places/tests/favicons/test_replaceFaviconData.js264
-rw-r--r--toolkit/components/places/tests/favicons/test_replaceFaviconDataFromDataURL.js352
-rw-r--r--toolkit/components/places/tests/favicons/xpcshell.ini32
27 files changed, 1268 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/favicons/.eslintrc.js b/toolkit/components/places/tests/favicons/.eslintrc.js
new file mode 100644
index 000000000..d35787cd2
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/toolkit/components/places/tests/favicons/expected-favicon-big32.jpg.png b/toolkit/components/places/tests/favicons/expected-favicon-big32.jpg.png
new file mode 100644
index 000000000..723008771
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/expected-favicon-big32.jpg.png
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/expected-favicon-big4.jpg.png b/toolkit/components/places/tests/favicons/expected-favicon-big4.jpg.png
new file mode 100644
index 000000000..9932c18fb
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/expected-favicon-big4.jpg.png
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/expected-favicon-big48.ico.png b/toolkit/components/places/tests/favicons/expected-favicon-big48.ico.png
new file mode 100644
index 000000000..9f16bef43
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/expected-favicon-big48.ico.png
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/expected-favicon-big64.png.png b/toolkit/components/places/tests/favicons/expected-favicon-big64.png.png
new file mode 100644
index 000000000..ed158d161
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/expected-favicon-big64.png.png
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/expected-favicon-scale160x3.jpg.png b/toolkit/components/places/tests/favicons/expected-favicon-scale160x3.jpg.png
new file mode 100644
index 000000000..585c9e897
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/expected-favicon-scale160x3.jpg.png
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/expected-favicon-scale3x160.jpg.png b/toolkit/components/places/tests/favicons/expected-favicon-scale3x160.jpg.png
new file mode 100644
index 000000000..e07dabc79
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/expected-favicon-scale3x160.jpg.png
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/favicon-big16.ico b/toolkit/components/places/tests/favicons/favicon-big16.ico
new file mode 100644
index 000000000..d44438903
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/favicon-big16.ico
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/favicon-big32.jpg b/toolkit/components/places/tests/favicons/favicon-big32.jpg
new file mode 100644
index 000000000..b2131bf0c
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/favicon-big32.jpg
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/favicon-big4.jpg b/toolkit/components/places/tests/favicons/favicon-big4.jpg
new file mode 100644
index 000000000..b84fcd35a
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/favicon-big4.jpg
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/favicon-big48.ico b/toolkit/components/places/tests/favicons/favicon-big48.ico
new file mode 100644
index 000000000..f22522411
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/favicon-big48.ico
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/favicon-big64.png b/toolkit/components/places/tests/favicons/favicon-big64.png
new file mode 100644
index 000000000..2756cf0cb
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/favicon-big64.png
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/favicon-normal16.png b/toolkit/components/places/tests/favicons/favicon-normal16.png
new file mode 100644
index 000000000..62b69a3d0
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/favicon-normal16.png
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/favicon-normal32.png b/toolkit/components/places/tests/favicons/favicon-normal32.png
new file mode 100644
index 000000000..5535363c9
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/favicon-normal32.png
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/favicon-scale160x3.jpg b/toolkit/components/places/tests/favicons/favicon-scale160x3.jpg
new file mode 100644
index 000000000..422ee7ea0
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/favicon-scale160x3.jpg
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/favicon-scale3x160.jpg b/toolkit/components/places/tests/favicons/favicon-scale3x160.jpg
new file mode 100644
index 000000000..e8514966a
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/favicon-scale3x160.jpg
Binary files differ
diff --git a/toolkit/components/places/tests/favicons/head_favicons.js b/toolkit/components/places/tests/favicons/head_favicons.js
new file mode 100644
index 000000000..cc81791e8
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/head_favicons.js
@@ -0,0 +1,105 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cr = Components.results;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+// Import common head.
+{
+ let commonFile = do_get_file("../head_common.js", false);
+ let uri = Services.io.newFileURI(commonFile);
+ Services.scriptloader.loadSubScript(uri.spec, this);
+}
+
+// Put any other stuff relative to this test folder below.
+
+
+// This error icon must stay in sync with FAVICON_ERRORPAGE_URL in
+// nsIFaviconService.idl, aboutCertError.xhtml and netError.xhtml.
+const FAVICON_ERRORPAGE_URI =
+ NetUtil.newURI("chrome://global/skin/icons/warning-16.png");
+
+/**
+ * Waits for the first OnPageChanged notification for ATTRIBUTE_FAVICON, and
+ * verifies that it matches the expected page URI and associated favicon URI.
+ *
+ * This function also double-checks the GUID parameter of the notification.
+ *
+ * @param aExpectedPageURI
+ * nsIURI object of the page whose favicon should change.
+ * @param aExpectedFaviconURI
+ * nsIURI object of the newly associated favicon.
+ * @param aCallback
+ * This function is called after the check finished.
+ */
+function waitForFaviconChanged(aExpectedPageURI, aExpectedFaviconURI,
+ aCallback) {
+ let historyObserver = {
+ __proto__: NavHistoryObserver.prototype,
+ onPageChanged: function WFFC_onPageChanged(aURI, aWhat, aValue, aGUID) {
+ if (aWhat != Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
+ return;
+ }
+ PlacesUtils.history.removeObserver(this);
+
+ do_check_true(aURI.equals(aExpectedPageURI));
+ do_check_eq(aValue, aExpectedFaviconURI.spec);
+ do_check_guid_for_uri(aURI, aGUID);
+ aCallback();
+ }
+ };
+ PlacesUtils.history.addObserver(historyObserver, false);
+}
+
+/**
+ * Checks that the favicon for the given page matches the provided data.
+ *
+ * @param aPageURI
+ * nsIURI object for the page to check.
+ * @param aExpectedMimeType
+ * Expected MIME type of the icon, for example "image/png".
+ * @param aExpectedData
+ * Expected icon data, expressed as an array of byte values.
+ * @param aCallback
+ * This function is called after the check finished.
+ */
+function checkFaviconDataForPage(aPageURI, aExpectedMimeType, aExpectedData,
+ aCallback) {
+ PlacesUtils.favicons.getFaviconDataForPage(aPageURI,
+ function (aURI, aDataLen, aData, aMimeType) {
+ do_check_eq(aExpectedMimeType, aMimeType);
+ do_check_true(compareArrays(aExpectedData, aData));
+ do_check_guid_for_uri(aPageURI);
+ aCallback();
+ });
+}
+
+/**
+ * Checks that the given page has no associated favicon.
+ *
+ * @param aPageURI
+ * nsIURI object for the page to check.
+ * @param aCallback
+ * This function is called after the check finished.
+ */
+function checkFaviconMissingForPage(aPageURI, aCallback) {
+ PlacesUtils.favicons.getFaviconURLForPage(aPageURI,
+ function (aURI, aDataLen, aData, aMimeType) {
+ do_check_true(aURI === null);
+ aCallback();
+ });
+}
+
+function promiseFaviconMissingForPage(aPageURI) {
+ return new Promise(resolve => checkFaviconMissingForPage(aPageURI, resolve));
+}
+
+function promiseFaviconChanged(aExpectedPageURI, aExpectedFaviconURI) {
+ return new Promise(resolve => waitForFaviconChanged(aExpectedPageURI, aExpectedFaviconURI, resolve));
+}
diff --git a/toolkit/components/places/tests/favicons/test_expireAllFavicons.js b/toolkit/components/places/tests/favicons/test_expireAllFavicons.js
new file mode 100644
index 000000000..c5d8edfdd
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/test_expireAllFavicons.js
@@ -0,0 +1,39 @@
+/**
+ * This file tests that favicons are correctly expired by expireAllFavicons.
+ */
+
+"use strict";
+
+const TEST_PAGE_URI = NetUtil.newURI("http://example.com/");
+const BOOKMARKED_PAGE_URI = NetUtil.newURI("http://example.com/bookmarked");
+
+add_task(function* test_expireAllFavicons() {
+ // Add a visited page.
+ yield PlacesTestUtils.addVisits({ uri: TEST_PAGE_URI, transition: TRANSITION_TYPED });
+
+ // Set a favicon for our test page.
+ yield promiseSetIconForPage(TEST_PAGE_URI, SMALLPNG_DATA_URI);
+
+ // Add a page with a bookmark.
+ yield PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ url: BOOKMARKED_PAGE_URI,
+ title: "Test bookmark"
+ });
+
+ // Set a favicon for our bookmark.
+ yield promiseSetIconForPage(BOOKMARKED_PAGE_URI, SMALLPNG_DATA_URI);
+
+ // Start expiration only after data has been saved in the database.
+ let promise = promiseTopicObserved(PlacesUtils.TOPIC_FAVICONS_EXPIRED);
+ PlacesUtils.favicons.expireAllFavicons();
+ yield promise;
+
+ // Check that the favicons for the pages we added were removed.
+ yield promiseFaviconMissingForPage(TEST_PAGE_URI);
+ yield promiseFaviconMissingForPage(BOOKMARKED_PAGE_URI);
+});
+
+function run_test() {
+ run_next_test();
+}
diff --git a/toolkit/components/places/tests/favicons/test_favicons_conversions.js b/toolkit/components/places/tests/favicons/test_favicons_conversions.js
new file mode 100644
index 000000000..fa0d332ec
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/test_favicons_conversions.js
@@ -0,0 +1,131 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * This file tests the image conversions done by the favicon service.
+ */
+
+// Globals
+
+// The pixel values we get on Windows are sometimes +/- 1 value compared to
+// other platforms, so we need to skip some image content tests.
+var isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
+
+/**
+ * Checks the conversion of the given test image file.
+ *
+ * @param aFileName
+ * File that contains the favicon image, located in the test folder.
+ * @param aFileMimeType
+ * MIME type of the image contained in the file.
+ * @param aFileLength
+ * Expected length of the file.
+ * @param aExpectConversion
+ * If false, the icon should be stored as is. If true, the expected data
+ * is loaded from a file named "expected-" + aFileName + ".png".
+ * @param aVaryOnWindows
+ * Indicates that the content of the converted image can be different on
+ * Windows and should not be checked on that platform.
+ * @param aCallback
+ * This function is called after the check finished.
+ */
+function checkFaviconDataConversion(aFileName, aFileMimeType, aFileLength,
+ aExpectConversion, aVaryOnWindows,
+ aCallback) {
+ let pageURI = NetUtil.newURI("http://places.test/page/" + aFileName);
+ PlacesTestUtils.addVisits({ uri: pageURI, transition: TRANSITION_TYPED }).then(
+ function () {
+ let faviconURI = NetUtil.newURI("http://places.test/icon/" + aFileName);
+ let fileData = readFileOfLength(aFileName, aFileLength);
+
+ PlacesUtils.favicons.replaceFaviconData(faviconURI, fileData, fileData.length,
+ aFileMimeType);
+ PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, faviconURI, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function CFDC_verify(aURI, aDataLen, aData, aMimeType) {
+ if (!aExpectConversion) {
+ do_check_true(compareArrays(aData, fileData));
+ do_check_eq(aMimeType, aFileMimeType);
+ } else {
+ if (!aVaryOnWindows || !isWindows) {
+ let expectedFile = do_get_file("expected-" + aFileName + ".png");
+ do_check_true(compareArrays(aData, readFileData(expectedFile)));
+ }
+ do_check_eq(aMimeType, "image/png");
+ }
+
+ aCallback();
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ });
+}
+
+// Tests
+
+function run_test() {
+ run_next_test();
+}
+
+add_test(function test_storing_a_normal_16x16_icon() {
+ // 16x16 png, 286 bytes.
+ // optimized: no
+ checkFaviconDataConversion("favicon-normal16.png", "image/png", 286,
+ false, false, run_next_test);
+});
+
+add_test(function test_storing_a_normal_32x32_icon() {
+ // 32x32 png, 344 bytes.
+ // optimized: no
+ checkFaviconDataConversion("favicon-normal32.png", "image/png", 344,
+ false, false, run_next_test);
+});
+
+add_test(function test_storing_a_big_16x16_icon() {
+ // in: 16x16 ico, 1406 bytes.
+ // optimized: no
+ checkFaviconDataConversion("favicon-big16.ico", "image/x-icon", 1406,
+ false, false, run_next_test);
+});
+
+add_test(function test_storing_an_oversize_4x4_icon() {
+ // in: 4x4 jpg, 4751 bytes.
+ // optimized: yes
+ checkFaviconDataConversion("favicon-big4.jpg", "image/jpeg", 4751,
+ true, false, run_next_test);
+});
+
+add_test(function test_storing_an_oversize_32x32_icon() {
+ // in: 32x32 jpg, 3494 bytes.
+ // optimized: yes
+ checkFaviconDataConversion("favicon-big32.jpg", "image/jpeg", 3494,
+ true, true, run_next_test);
+});
+
+add_test(function test_storing_an_oversize_48x48_icon() {
+ // in: 48x48 ico, 56646 bytes.
+ // (howstuffworks.com icon, contains 13 icons with sizes from 16x16 to
+ // 48x48 in varying depths)
+ // optimized: yes
+ checkFaviconDataConversion("favicon-big48.ico", "image/x-icon", 56646,
+ true, false, run_next_test);
+});
+
+add_test(function test_storing_an_oversize_64x64_icon() {
+ // in: 64x64 png, 10698 bytes.
+ // optimized: yes
+ checkFaviconDataConversion("favicon-big64.png", "image/png", 10698,
+ true, false, run_next_test);
+});
+
+add_test(function test_scaling_an_oversize_160x3_icon() {
+ // in: 160x3 jpg, 5095 bytes.
+ // optimized: yes
+ checkFaviconDataConversion("favicon-scale160x3.jpg", "image/jpeg", 5095,
+ true, false, run_next_test);
+});
+
+add_test(function test_scaling_an_oversize_3x160_icon() {
+ // in: 3x160 jpg, 5059 bytes.
+ // optimized: yes
+ checkFaviconDataConversion("favicon-scale3x160.jpg", "image/jpeg", 5059,
+ true, false, run_next_test);
+});
diff --git a/toolkit/components/places/tests/favicons/test_getFaviconDataForPage.js b/toolkit/components/places/tests/favicons/test_getFaviconDataForPage.js
new file mode 100644
index 000000000..73eea7436
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/test_getFaviconDataForPage.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * This file tests getFaviconDataForPage.
+ */
+
+// Globals
+
+const FAVICON_URI = NetUtil.newURI(do_get_file("favicon-normal32.png"));
+const FAVICON_DATA = readFileData(do_get_file("favicon-normal32.png"));
+const FAVICON_MIMETYPE = "image/png";
+
+// Tests
+
+function run_test()
+{
+ // Check that the favicon loaded correctly before starting the actual tests.
+ do_check_eq(FAVICON_DATA.length, 344);
+ run_next_test();
+}
+
+add_test(function test_normal()
+{
+ let pageURI = NetUtil.newURI("http://example.com/normal");
+
+ PlacesTestUtils.addVisits(pageURI).then(function () {
+ PlacesUtils.favicons.setAndFetchFaviconForPage(
+ pageURI, FAVICON_URI, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function () {
+ PlacesUtils.favicons.getFaviconDataForPage(pageURI,
+ function (aURI, aDataLen, aData, aMimeType) {
+ do_check_true(aURI.equals(FAVICON_URI));
+ do_check_eq(FAVICON_DATA.length, aDataLen);
+ do_check_true(compareArrays(FAVICON_DATA, aData));
+ do_check_eq(FAVICON_MIMETYPE, aMimeType);
+ run_next_test();
+ });
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ });
+});
+
+add_test(function test_missing()
+{
+ let pageURI = NetUtil.newURI("http://example.com/missing");
+
+ PlacesUtils.favicons.getFaviconDataForPage(pageURI,
+ function (aURI, aDataLen, aData, aMimeType) {
+ // Check also the expected data types.
+ do_check_true(aURI === null);
+ do_check_true(aDataLen === 0);
+ do_check_true(aData.length === 0);
+ do_check_true(aMimeType === "");
+ run_next_test();
+ });
+});
diff --git a/toolkit/components/places/tests/favicons/test_getFaviconURLForPage.js b/toolkit/components/places/tests/favicons/test_getFaviconURLForPage.js
new file mode 100644
index 000000000..fb2e23ff9
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/test_getFaviconURLForPage.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * This file tests getFaviconURLForPage.
+ */
+
+// Tests
+
+function run_test()
+{
+ run_next_test();
+}
+
+add_test(function test_normal()
+{
+ let pageURI = NetUtil.newURI("http://example.com/normal");
+
+ PlacesTestUtils.addVisits(pageURI).then(function () {
+ PlacesUtils.favicons.setAndFetchFaviconForPage(
+ pageURI, SMALLPNG_DATA_URI, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function () {
+ PlacesUtils.favicons.getFaviconURLForPage(pageURI,
+ function (aURI, aDataLen, aData, aMimeType) {
+ do_check_true(aURI.equals(SMALLPNG_DATA_URI));
+
+ // Check also the expected data types.
+ do_check_true(aDataLen === 0);
+ do_check_true(aData.length === 0);
+ do_check_true(aMimeType === "");
+ run_next_test();
+ });
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ });
+});
+
+add_test(function test_missing()
+{
+ let pageURI = NetUtil.newURI("http://example.com/missing");
+
+ PlacesUtils.favicons.getFaviconURLForPage(pageURI,
+ function (aURI, aDataLen, aData, aMimeType) {
+ // Check also the expected data types.
+ do_check_true(aURI === null);
+ do_check_true(aDataLen === 0);
+ do_check_true(aData.length === 0);
+ do_check_true(aMimeType === "");
+ run_next_test();
+ });
+});
diff --git a/toolkit/components/places/tests/favicons/test_moz-anno_favicon_mime_type.js b/toolkit/components/places/tests/favicons/test_moz-anno_favicon_mime_type.js
new file mode 100644
index 000000000..d055d8d61
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/test_moz-anno_favicon_mime_type.js
@@ -0,0 +1,90 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This test ensures that the mime type is set for moz-anno channels of favicons
+ * properly. Added with work in bug 481227.
+ */
+
+// Constants
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+const testFaviconData = "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%04gAMA%00%00%AF%C87%05%8A%E9%00%00%00%19tEXtSoftware%00Adobe%20ImageReadyq%C9e%3C%00%00%01%D6IDATx%DAb%FC%FF%FF%3F%03%25%00%20%80%98%909%EF%DF%BFg%EF%EC%EC%FC%AD%AC%AC%FC%DF%95%91%F1%BF%89%89%C9%7F%20%FF%D7%EA%D5%AB%B7%DF%BBwO%16%9B%01%00%01%C4%00r%01%08%9F9s%C6%CD%D8%D8%F8%BF%0B%03%C3%FF3%40%BC%0A%88%EF%02q%1A%10%BB%40%F1%AAU%ABv%C1%D4%C30%40%00%81%89%993g%3E%06%1A%F6%3F%14%AA%11D%97%03%F1%7Fc%08%0D%E2%2B))%FD%17%04%89%A1%19%00%10%40%0C%D00%F8%0F3%00%C8%F8%BF%1B%E4%0Ac%88a%E5%60%17%19%FF%0F%0D%0D%05%1B%02v%D9%DD%BB%0A0%03%00%02%08%AC%B9%A3%A3%E3%17%03%D4v%90%01%EF%18%106%C3%0Cz%07%C5%BB%A1%DE%82y%07%20%80%A0%A6%08B%FCn%0C1%60%26%D4%20d%C3VA%C3%06%26%BE%0A%EA-%80%00%82%B9%E0%F7L4%0D%EF%90%F8%C6%60%2F%0A%82%BD%01%13%07%0700%D0%01%02%88%11%E4%02P%B41%DC%BB%C7%D0%014%0D%E8l%06W%20%06%BA%88%A1%1C%1AS%15%40%7C%16%CA6.%2Fgx%BFg%0F%83%CB%D9%B3%0C%7B%80%7C%80%00%02%BB%00%E8%9F%ED%20%1B%3A%A0%A6%9F%81%DA%DC%01%C5%B0%80%ED%80%FA%BF%BC%BC%FC%3F%83%12%90%9D%96%F6%1F%20%80%18%DE%BD%7B%C7%0E%8E%05AD%20%FEGr%A6%A0%A0%E0%7F%25P%80%02%9D%0F%D28%13%18%23%C6%C0%B0%02E%3D%C8%F5%00%01%04%8F%05P%A8%BA%40my%87%E4%12c%A8%8D%20%8B%D0%D3%00%08%03%04%10%9C%01R%E4%82d%3B%C8%A0%99%C6%90%90%C6%A5%19%84%01%02%08%9E%17%80%C9x%F7%7B%A0%DBVC%F9%A0%C0%5C%7D%16%2C%CE%00%F4%C6O%5C%99%09%20%800L%04y%A5%03%1A%95%A0%80%05%05%14.%DBA%18%20%80%18)%CD%CE%00%01%06%00%0C'%94%C7%C0k%C9%2C%00%00%00%00IEND%AEB%60%82";
+const moz_anno_favicon_prefix = "moz-anno:favicon:";
+
+// streamListener
+
+function streamListener(aExpectedContentType)
+{
+ this._expectedContentType = aExpectedContentType;
+}
+streamListener.prototype =
+{
+ onStartRequest: function(aRequest, aContext)
+ {
+ // We have other tests that make sure the data is what we expect. We just
+ // need to check the content type here.
+ let channel = aRequest.QueryInterface(Ci.nsIChannel);
+ dump("*** Checking " + channel.URI.spec + "\n");
+ do_check_eq(channel.contentType, this._expectedContentType);
+
+ // If we somehow throw before doing the above check, the test will pass, so
+ // we do this for extra sanity.
+ this._checked = true;
+ },
+ onStopRequest: function()
+ {
+ do_check_true(this._checked);
+ do_test_finished();
+ },
+ onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount)
+ {
+ aRequest.cancel(Cr.NS_ERROR_ABORT);
+ }
+};
+
+// Test Runner
+
+function run_test()
+{
+ let fs = Cc["@mozilla.org/browser/favicon-service;1"].
+ getService(Ci.nsIFaviconService);
+
+ // Test that the default icon has the content type of image/png.
+ let channel = NetUtil.newChannel({
+ uri: fs.defaultFavicon,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON
+ });
+ channel.asyncOpen2(new streamListener("image/png"));
+ do_test_pending();
+
+ // Test URI that we don't know anything about. Will end up being the default
+ // icon, so expect image/png.
+ channel = NetUtil.newChannel({
+ uri: moz_anno_favicon_prefix + "http://mozilla.org",
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON
+ });
+ channel.asyncOpen2(new streamListener("image/png"));
+ do_test_pending();
+
+ // Test that the content type of a favicon we add ends up being image/png.
+ let testURI = uri("http://mozilla.org/");
+ // Add the data before opening
+ fs.replaceFaviconDataFromDataURL(testURI, testFaviconData,
+ (Date.now() + 60 * 60 * 24 * 1000) * 1000,
+ Services.scriptSecurityManager.getSystemPrincipal());
+
+ // Open the channel
+ channel = NetUtil.newChannel({
+ uri: moz_anno_favicon_prefix + testURI.spec,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON
+ });
+ channel.asyncOpen2(new streamListener("image/png"));
+ do_test_pending();
+}
diff --git a/toolkit/components/places/tests/favicons/test_page-icon_protocol.js b/toolkit/components/places/tests/favicons/test_page-icon_protocol.js
new file mode 100644
index 000000000..5533d5135
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/test_page-icon_protocol.js
@@ -0,0 +1,66 @@
+const ICON_DATA = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
+const TEST_URI = NetUtil.newURI("http://mozilla.org/");
+const ICON_URI = NetUtil.newURI("http://mozilla.org/favicon.ico");
+
+function fetchIconForSpec(spec) {
+ return new Promise((resolve, reject) => {
+ NetUtil.asyncFetch({
+ uri: NetUtil.newURI("page-icon:" + TEST_URI.spec),
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON
+ }, (input, status, request) => {
+ if (!Components.isSuccessCode(status)) {
+ reject(new Error("unable to load icon"));
+ return;
+ }
+
+ try {
+ let data = NetUtil.readInputStreamToString(input, input.available());
+ let contentType = request.QueryInterface(Ci.nsIChannel).contentType;
+ input.close();
+ resolve({ data, contentType });
+ } catch (ex) {
+ reject(ex);
+ }
+ });
+ });
+}
+
+var gDefaultFavicon;
+var gFavicon;
+
+add_task(function* setup() {
+ yield PlacesTestUtils.addVisits({ uri: TEST_URI });
+
+ PlacesUtils.favicons.replaceFaviconDataFromDataURL(
+ ICON_URI, ICON_DATA, (Date.now() + 8640000) * 1000,
+ Services.scriptSecurityManager.getSystemPrincipal());
+
+ yield new Promise(resolve => {
+ PlacesUtils.favicons.setAndFetchFaviconForPage(
+ TEST_URI, ICON_URI, false,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ resolve, Services.scriptSecurityManager.getSystemPrincipal());
+ });
+
+ gDefaultFavicon = yield fetchIconForSpec(PlacesUtils.favicons.defaultFavicon);
+ gFavicon = yield fetchIconForSpec(ICON_DATA);
+});
+
+add_task(function* known_url() {
+ let {data, contentType} = yield fetchIconForSpec(TEST_URI.spec);
+ Assert.equal(contentType, gFavicon.contentType);
+ Assert.ok(data == gFavicon.data, "Got the favicon data");
+});
+
+add_task(function* unknown_url() {
+ let {data, contentType} = yield fetchIconForSpec("http://www.moz.org/");
+ Assert.equal(contentType, gDefaultFavicon.contentType);
+ Assert.ok(data == gDefaultFavicon.data, "Got the default favicon data");
+});
+
+add_task(function* invalid_url() {
+ let {data, contentType} = yield fetchIconForSpec("test");
+ Assert.equal(contentType, gDefaultFavicon.contentType);
+ Assert.ok(data == gDefaultFavicon.data, "Got the default favicon data");
+});
diff --git a/toolkit/components/places/tests/favicons/test_query_result_favicon_changed_on_child.js b/toolkit/components/places/tests/favicons/test_query_result_favicon_changed_on_child.js
new file mode 100644
index 000000000..df61c22cd
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/test_query_result_favicon_changed_on_child.js
@@ -0,0 +1,74 @@
+/**
+ * Test for bug 451499 <https://bugzilla.mozilla.org/show_bug.cgi?id=451499>:
+ * Wrong folder icon appears on smart bookmarks.
+ */
+
+"use strict";
+
+const PAGE_URI = NetUtil.newURI("http://example.com/test_query_result");
+
+add_task(function* test_query_result_favicon_changed_on_child() {
+ // Bookmark our test page, so it will appear in the query resultset.
+ yield PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ title: "test_bookmark",
+ url: PAGE_URI
+ });
+
+ // Get the last 10 bookmarks added to the menu or the toolbar.
+ let query = PlacesUtils.history.getNewQuery();
+ query.setFolders([PlacesUtils.bookmarksMenuFolderId,
+ PlacesUtils.toolbarFolderId], 2);
+
+ let options = PlacesUtils.history.getNewQueryOptions();
+ options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
+ options.maxResults = 10;
+ options.excludeQueries = 1;
+ options.sortingMode = options.SORT_BY_DATE_DESCENDING;
+
+ let result = PlacesUtils.history.executeQuery(query, options);
+ let resultObserver = {
+ __proto__: NavHistoryResultObserver.prototype,
+ containerStateChanged(aContainerNode, aOldState, aNewState) {
+ if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
+ // We set a favicon on PAGE_URI while the container is open. The
+ // favicon for the page must have data associated with it in order for
+ // the icon changed notifications to be sent, so we use a valid image
+ // data URI.
+ PlacesUtils.favicons.setAndFetchFaviconForPage(PAGE_URI,
+ SMALLPNG_DATA_URI,
+ false,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ null,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ }
+ },
+ nodeIconChanged(aNode) {
+ do_throw("The icon should be set only for the page," +
+ " not for the containing query.");
+ }
+ };
+ result.addObserver(resultObserver, false);
+
+ // Open the container and wait for containerStateChanged. We should start
+ // observing before setting |containerOpen| as that's caused by the
+ // setAndFetchFaviconForPage() call caused by the containerStateChanged
+ // observer above.
+ let promise = promiseFaviconChanged(PAGE_URI, SMALLPNG_DATA_URI);
+ result.root.containerOpen = true;
+ yield promise;
+
+ // We must wait for the asynchronous database thread to finish the
+ // operation, and then for the main thread to process any pending
+ // notifications that came from the asynchronous thread, before we can be
+ // sure that nodeIconChanged was not invoked in the meantime.
+ yield PlacesTestUtils.promiseAsyncUpdates();
+ result.removeObserver(resultObserver);
+
+ // Free the resources immediately.
+ result.root.containerOpen = false;
+});
+
+function run_test() {
+ run_next_test();
+}
diff --git a/toolkit/components/places/tests/favicons/test_replaceFaviconData.js b/toolkit/components/places/tests/favicons/test_replaceFaviconData.js
new file mode 100644
index 000000000..ac53e70e9
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/test_replaceFaviconData.js
@@ -0,0 +1,264 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests for mozIAsyncFavicons::replaceFaviconData()
+ */
+
+var iconsvc = PlacesUtils.favicons;
+var histsvc = PlacesUtils.history;
+var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+
+var originalFavicon = {
+ file: do_get_file("favicon-normal16.png"),
+ uri: uri(do_get_file("favicon-normal16.png")),
+ data: readFileData(do_get_file("favicon-normal16.png")),
+ mimetype: "image/png"
+};
+
+var uniqueFaviconId = 0;
+function createFavicon(fileName) {
+ let tempdir = Services.dirsvc.get("TmpD", Ci.nsILocalFile);
+
+ // remove any existing file at the path we're about to copy to
+ let outfile = tempdir.clone();
+ outfile.append(fileName);
+ try { outfile.remove(false); } catch (e) {}
+
+ originalFavicon.file.copyToFollowingLinks(tempdir, fileName);
+
+ let stream = Cc["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+ stream.init(outfile, 0x02 | 0x08 | 0x10, 0o600, 0);
+
+ // append some data that sniffers/encoders will ignore that will distinguish
+ // the different favicons we'll create
+ uniqueFaviconId++;
+ let uniqueStr = "uid:" + uniqueFaviconId;
+ stream.write(uniqueStr, uniqueStr.length);
+ stream.close();
+
+ do_check_eq(outfile.leafName.substr(0, fileName.length), fileName);
+
+ return {
+ file: outfile,
+ uri: uri(outfile),
+ data: readFileData(outfile),
+ mimetype: "image/png"
+ };
+}
+
+function checkCallbackSucceeded(callbackMimetype, callbackData, sourceMimetype, sourceData) {
+ do_check_eq(callbackMimetype, sourceMimetype);
+ do_check_true(compareArrays(callbackData, sourceData));
+}
+
+function run_test() {
+ // check that the favicon loaded correctly
+ do_check_eq(originalFavicon.data.length, 286);
+ run_next_test();
+}
+
+add_task(function* test_replaceFaviconData_validHistoryURI() {
+ do_print("test replaceFaviconData for valid history uri");
+
+ let pageURI = uri("http://test1.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let favicon = createFavicon("favicon1.png");
+
+ iconsvc.replaceFaviconData(favicon.uri, favicon.data, favicon.data.length,
+ favicon.mimetype);
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(pageURI, favicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconData_validHistoryURI_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, favicon.mimetype, favicon.data);
+ checkFaviconDataForPage(
+ pageURI, favicon.mimetype, favicon.data,
+ function test_replaceFaviconData_validHistoryURI_callback() {
+ favicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ }, systemPrincipal);
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconData_overrideDefaultFavicon() {
+ do_print("test replaceFaviconData to override a later setAndFetchFaviconForPage");
+
+ let pageURI = uri("http://test2.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let firstFavicon = createFavicon("favicon2.png");
+ let secondFavicon = createFavicon("favicon3.png");
+
+ iconsvc.replaceFaviconData(
+ firstFavicon.uri, secondFavicon.data, secondFavicon.data.length,
+ secondFavicon.mimetype);
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, firstFavicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconData_overrideDefaultFavicon_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+ checkFaviconDataForPage(
+ pageURI, secondFavicon.mimetype, secondFavicon.data,
+ function test_replaceFaviconData_overrideDefaultFavicon_callback() {
+ firstFavicon.file.remove(false);
+ secondFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ }, systemPrincipal);
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconData_replaceExisting() {
+ do_print("test replaceFaviconData to override a previous setAndFetchFaviconForPage");
+
+ let pageURI = uri("http://test3.bar");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let firstFavicon = createFavicon("favicon4.png");
+ let secondFavicon = createFavicon("favicon5.png");
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, firstFavicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconData_replaceExisting_firstSet_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, firstFavicon.mimetype, firstFavicon.data);
+ checkFaviconDataForPage(
+ pageURI, firstFavicon.mimetype, firstFavicon.data,
+ function test_replaceFaviconData_overrideDefaultFavicon_firstCallback() {
+ iconsvc.replaceFaviconData(
+ firstFavicon.uri, secondFavicon.data, secondFavicon.data.length,
+ secondFavicon.mimetype);
+ PlacesTestUtils.promiseAsyncUpdates().then(() => {
+ checkFaviconDataForPage(
+ pageURI, secondFavicon.mimetype, secondFavicon.data,
+ function test_replaceFaviconData_overrideDefaultFavicon_secondCallback() {
+ firstFavicon.file.remove(false);
+ secondFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ }, systemPrincipal);
+ });
+ });
+ }, systemPrincipal);
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconData_unrelatedReplace() {
+ do_print("test replaceFaviconData to not make unrelated changes");
+
+ let pageURI = uri("http://test4.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let favicon = createFavicon("favicon6.png");
+ let unrelatedFavicon = createFavicon("favicon7.png");
+
+ iconsvc.replaceFaviconData(
+ unrelatedFavicon.uri, unrelatedFavicon.data, unrelatedFavicon.data.length,
+ unrelatedFavicon.mimetype);
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, favicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconData_unrelatedReplace_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, favicon.mimetype, favicon.data);
+ checkFaviconDataForPage(
+ pageURI, favicon.mimetype, favicon.data,
+ function test_replaceFaviconData_unrelatedReplace_callback() {
+ favicon.file.remove(false);
+ unrelatedFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ }, systemPrincipal);
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconData_badInputs() {
+ do_print("test replaceFaviconData to throw on bad inputs");
+
+ let favicon = createFavicon("favicon8.png");
+
+ let ex = null;
+ try {
+ iconsvc.replaceFaviconData(
+ favicon.uri, favicon.data, favicon.data.length, "");
+ } catch (e) {
+ ex = e;
+ } finally {
+ do_check_true(!!ex);
+ }
+
+ ex = null;
+ try {
+ iconsvc.replaceFaviconData(
+ null, favicon.data, favicon.data.length, favicon.mimeType);
+ } catch (e) {
+ ex = e;
+ } finally {
+ do_check_true(!!ex);
+ }
+
+ ex = null;
+ try {
+ iconsvc.replaceFaviconData(
+ favicon.uri, null, 0, favicon.mimeType);
+ } catch (e) {
+ ex = e;
+ } finally {
+ do_check_true(!!ex);
+ }
+
+ favicon.file.remove(false);
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconData_twiceReplace() {
+ do_print("test replaceFaviconData on multiple replacements");
+
+ let pageURI = uri("http://test5.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let firstFavicon = createFavicon("favicon9.png");
+ let secondFavicon = createFavicon("favicon10.png");
+
+ iconsvc.replaceFaviconData(
+ firstFavicon.uri, firstFavicon.data, firstFavicon.data.length,
+ firstFavicon.mimetype);
+ iconsvc.replaceFaviconData(
+ firstFavicon.uri, secondFavicon.data, secondFavicon.data.length,
+ secondFavicon.mimetype);
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, firstFavicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconData_twiceReplace_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+ checkFaviconDataForPage(
+ pageURI, secondFavicon.mimetype, secondFavicon.data,
+ function test_replaceFaviconData_twiceReplace_callback() {
+ firstFavicon.file.remove(false);
+ secondFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ }, systemPrincipal);
+ }, systemPrincipal);
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
diff --git a/toolkit/components/places/tests/favicons/test_replaceFaviconDataFromDataURL.js b/toolkit/components/places/tests/favicons/test_replaceFaviconDataFromDataURL.js
new file mode 100644
index 000000000..69a5ba852
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/test_replaceFaviconDataFromDataURL.js
@@ -0,0 +1,352 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests for mozIAsyncFavicons::replaceFaviconData()
+ */
+
+var iconsvc = PlacesUtils.favicons;
+var histsvc = PlacesUtils.history;
+
+var originalFavicon = {
+ file: do_get_file("favicon-normal16.png"),
+ uri: uri(do_get_file("favicon-normal16.png")),
+ data: readFileData(do_get_file("favicon-normal16.png")),
+ mimetype: "image/png"
+};
+
+var uniqueFaviconId = 0;
+function createFavicon(fileName) {
+ let tempdir = Services.dirsvc.get("TmpD", Ci.nsILocalFile);
+
+ // remove any existing file at the path we're about to copy to
+ let outfile = tempdir.clone();
+ outfile.append(fileName);
+ try { outfile.remove(false); } catch (e) {}
+
+ originalFavicon.file.copyToFollowingLinks(tempdir, fileName);
+
+ let stream = Cc["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+ stream.init(outfile, 0x02 | 0x08 | 0x10, 0o600, 0);
+
+ // append some data that sniffers/encoders will ignore that will distinguish
+ // the different favicons we'll create
+ uniqueFaviconId++;
+ let uniqueStr = "uid:" + uniqueFaviconId;
+ stream.write(uniqueStr, uniqueStr.length);
+ stream.close();
+
+ do_check_eq(outfile.leafName.substr(0, fileName.length), fileName);
+
+ return {
+ file: outfile,
+ uri: uri(outfile),
+ data: readFileData(outfile),
+ mimetype: "image/png"
+ };
+}
+
+function createDataURLForFavicon(favicon) {
+ return "data:" + favicon.mimetype + ";base64," + toBase64(favicon.data);
+}
+
+function checkCallbackSucceeded(callbackMimetype, callbackData, sourceMimetype, sourceData) {
+ do_check_eq(callbackMimetype, sourceMimetype);
+ do_check_true(compareArrays(callbackData, sourceData));
+}
+
+function run_test() {
+ // check that the favicon loaded correctly
+ do_check_eq(originalFavicon.data.length, 286);
+ run_next_test();
+}
+
+add_task(function* test_replaceFaviconDataFromDataURL_validHistoryURI() {
+ do_print("test replaceFaviconDataFromDataURL for valid history uri");
+
+ let pageURI = uri("http://test1.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let favicon = createFavicon("favicon1.png");
+ iconsvc.replaceFaviconDataFromDataURL(favicon.uri, createDataURLForFavicon(favicon), 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(pageURI, favicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconDataFromDataURL_validHistoryURI_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, favicon.mimetype, favicon.data);
+ checkFaviconDataForPage(
+ pageURI, favicon.mimetype, favicon.data,
+ function test_replaceFaviconDataFromDataURL_validHistoryURI_callback() {
+ favicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconDataFromDataURL_overrideDefaultFavicon() {
+ do_print("test replaceFaviconDataFromDataURL to override a later setAndFetchFaviconForPage");
+
+ let pageURI = uri("http://test2.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let firstFavicon = createFavicon("favicon2.png");
+ let secondFavicon = createFavicon("favicon3.png");
+
+ iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon), 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, firstFavicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+ checkFaviconDataForPage(
+ pageURI, secondFavicon.mimetype, secondFavicon.data,
+ function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon_callback() {
+ firstFavicon.file.remove(false);
+ secondFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconDataFromDataURL_replaceExisting() {
+ do_print("test replaceFaviconDataFromDataURL to override a previous setAndFetchFaviconForPage");
+
+ let pageURI = uri("http://test3.bar");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let firstFavicon = createFavicon("favicon4.png");
+ let secondFavicon = createFavicon("favicon5.png");
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, firstFavicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconDataFromDataURL_replaceExisting_firstSet_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, firstFavicon.mimetype, firstFavicon.data);
+ checkFaviconDataForPage(
+ pageURI, firstFavicon.mimetype, firstFavicon.data,
+ function test_replaceFaviconDataFromDataURL_replaceExisting_firstCallback() {
+ iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon), 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ checkFaviconDataForPage(
+ pageURI, secondFavicon.mimetype, secondFavicon.data,
+ function test_replaceFaviconDataFromDataURL_replaceExisting_secondCallback() {
+ firstFavicon.file.remove(false);
+ secondFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ });
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconDataFromDataURL_unrelatedReplace() {
+ do_print("test replaceFaviconDataFromDataURL to not make unrelated changes");
+
+ let pageURI = uri("http://test4.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let favicon = createFavicon("favicon6.png");
+ let unrelatedFavicon = createFavicon("favicon7.png");
+
+ iconsvc.replaceFaviconDataFromDataURL(unrelatedFavicon.uri, createDataURLForFavicon(unrelatedFavicon), 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, favicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconDataFromDataURL_unrelatedReplace_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, favicon.mimetype, favicon.data);
+ checkFaviconDataForPage(
+ pageURI, favicon.mimetype, favicon.data,
+ function test_replaceFaviconDataFromDataURL_unrelatedReplace_callback() {
+ favicon.file.remove(false);
+ unrelatedFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconDataFromDataURL_badInputs() {
+ do_print("test replaceFaviconDataFromDataURL to throw on bad inputs");
+
+ let favicon = createFavicon("favicon8.png");
+
+ let ex = null;
+ try {
+ iconsvc.replaceFaviconDataFromDataURL(favicon.uri, "", 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ } catch (e) {
+ ex = e;
+ } finally {
+ do_check_true(!!ex);
+ }
+
+ ex = null;
+ try {
+ iconsvc.replaceFaviconDataFromDataURL(null, createDataURLForFavicon(favicon), 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ } catch (e) {
+ ex = e;
+ } finally {
+ do_check_true(!!ex);
+ }
+
+ favicon.file.remove(false);
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconDataFromDataURL_twiceReplace() {
+ do_print("test replaceFaviconDataFromDataURL on multiple replacements");
+
+ let pageURI = uri("http://test5.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let firstFavicon = createFavicon("favicon9.png");
+ let secondFavicon = createFavicon("favicon10.png");
+
+ iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(firstFavicon), 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon), 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, firstFavicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconDataFromDataURL_twiceReplace_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+ checkFaviconDataForPage(
+ pageURI, secondFavicon.mimetype, secondFavicon.data,
+ function test_replaceFaviconDataFromDataURL_twiceReplace_callback() {
+ firstFavicon.file.remove(false);
+ secondFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconDataFromDataURL_afterRegularAssign() {
+ do_print("test replaceFaviconDataFromDataURL after replaceFaviconData");
+
+ let pageURI = uri("http://test6.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let firstFavicon = createFavicon("favicon11.png");
+ let secondFavicon = createFavicon("favicon12.png");
+
+ iconsvc.replaceFaviconData(
+ firstFavicon.uri, firstFavicon.data, firstFavicon.data.length,
+ firstFavicon.mimetype);
+ iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon), 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, firstFavicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconDataFromDataURL_afterRegularAssign_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+ checkFaviconDataForPage(
+ pageURI, secondFavicon.mimetype, secondFavicon.data,
+ function test_replaceFaviconDataFromDataURL_afterRegularAssign_callback() {
+ firstFavicon.file.remove(false);
+ secondFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+add_task(function* test_replaceFaviconDataFromDataURL_beforeRegularAssign() {
+ do_print("test replaceFaviconDataFromDataURL before replaceFaviconData");
+
+ let pageURI = uri("http://test7.bar/");
+ yield PlacesTestUtils.addVisits(pageURI);
+
+ let firstFavicon = createFavicon("favicon13.png");
+ let secondFavicon = createFavicon("favicon14.png");
+
+ iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(firstFavicon), 0,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ iconsvc.replaceFaviconData(
+ firstFavicon.uri, secondFavicon.data, secondFavicon.data.length,
+ secondFavicon.mimetype);
+
+ let deferSetAndFetchFavicon = Promise.defer();
+ iconsvc.setAndFetchFaviconForPage(
+ pageURI, firstFavicon.uri, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ function test_replaceFaviconDataFromDataURL_beforeRegularAssign_check(aURI, aDataLen, aData, aMimeType) {
+ checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+ checkFaviconDataForPage(
+ pageURI, secondFavicon.mimetype, secondFavicon.data,
+ function test_replaceFaviconDataFromDataURL_beforeRegularAssign_callback() {
+ firstFavicon.file.remove(false);
+ secondFavicon.file.remove(false);
+ deferSetAndFetchFavicon.resolve();
+ });
+ }, Services.scriptSecurityManager.getSystemPrincipal());
+ yield deferSetAndFetchFavicon.promise;
+
+ yield PlacesTestUtils.clearHistory();
+});
+
+/* toBase64 copied from image/test/unit/test_encoder_png.js */
+
+/* Convert data (an array of integers) to a Base64 string. */
+const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +
+ '0123456789+/';
+const base64Pad = '=';
+function toBase64(data) {
+ let result = '';
+ let length = data.length;
+ let i;
+ // Convert every three bytes to 4 ascii characters.
+ for (i = 0; i < (length - 2); i += 3) {
+ result += toBase64Table[data[i] >> 2];
+ result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
+ result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
+ result += toBase64Table[data[i+2] & 0x3f];
+ }
+
+ // Convert the remaining 1 or 2 bytes, pad out to 4 characters.
+ if (length%3) {
+ i = length - (length%3);
+ result += toBase64Table[data[i] >> 2];
+ if ((length%3) == 2) {
+ result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
+ result += toBase64Table[(data[i+1] & 0x0f) << 2];
+ result += base64Pad;
+ } else {
+ result += toBase64Table[(data[i] & 0x03) << 4];
+ result += base64Pad + base64Pad;
+ }
+ }
+
+ return result;
+}
diff --git a/toolkit/components/places/tests/favicons/xpcshell.ini b/toolkit/components/places/tests/favicons/xpcshell.ini
new file mode 100644
index 000000000..851f193c7
--- /dev/null
+++ b/toolkit/components/places/tests/favicons/xpcshell.ini
@@ -0,0 +1,32 @@
+[DEFAULT]
+head = head_favicons.js
+tail =
+skip-if = toolkit == 'android'
+support-files =
+ expected-favicon-big32.jpg.png
+ expected-favicon-big4.jpg.png
+ expected-favicon-big48.ico.png
+ expected-favicon-big64.png.png
+ expected-favicon-scale160x3.jpg.png
+ expected-favicon-scale3x160.jpg.png
+ favicon-big16.ico
+ favicon-big32.jpg
+ favicon-big4.jpg
+ favicon-big48.ico
+ favicon-big64.png
+ favicon-normal16.png
+ favicon-normal32.png
+ favicon-scale160x3.jpg
+ favicon-scale3x160.jpg
+
+[test_expireAllFavicons.js]
+[test_favicons_conversions.js]
+# Bug 676989: test fails consistently on Android
+fail-if = os == "android"
+[test_getFaviconDataForPage.js]
+[test_getFaviconURLForPage.js]
+[test_moz-anno_favicon_mime_type.js]
+[test_page-icon_protocol.js]
+[test_query_result_favicon_changed_on_child.js]
+[test_replaceFaviconData.js]
+[test_replaceFaviconDataFromDataURL.js]