diff options
Diffstat (limited to 'toolkit/components/places/tests/browser')
49 files changed, 2287 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/browser/.eslintrc.js b/toolkit/components/places/tests/browser/.eslintrc.js new file mode 100644 index 000000000..7a41a9cde --- /dev/null +++ b/toolkit/components/places/tests/browser/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "../../../../../testing/mochitest/browser.eslintrc.js", + "../../../../../testing/mochitest/mochitest.eslintrc.js" + ] +}; diff --git a/toolkit/components/places/tests/browser/399606-history.go-0.html b/toolkit/components/places/tests/browser/399606-history.go-0.html new file mode 100644 index 000000000..039708ed7 --- /dev/null +++ b/toolkit/components/places/tests/browser/399606-history.go-0.html @@ -0,0 +1,11 @@ +<html> +<head> +<title>history.go(0)</title> +<script> +setTimeout('history.go(0)', 1000); +</script> +</head> +<body> +Testing history.go(0) +</body> +</html> diff --git a/toolkit/components/places/tests/browser/399606-httprefresh.html b/toolkit/components/places/tests/browser/399606-httprefresh.html new file mode 100644 index 000000000..e43455ee0 --- /dev/null +++ b/toolkit/components/places/tests/browser/399606-httprefresh.html @@ -0,0 +1,8 @@ +<html><head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> + +<meta http-equiv="refresh" content="1"> +<title>httprefresh</title> +</head><body> +Testing httprefresh +</body></html> diff --git a/toolkit/components/places/tests/browser/399606-location.reload.html b/toolkit/components/places/tests/browser/399606-location.reload.html new file mode 100644 index 000000000..0f46538cd --- /dev/null +++ b/toolkit/components/places/tests/browser/399606-location.reload.html @@ -0,0 +1,11 @@ +<html> +<head> +<title>location.reload()</title> +<script> +setTimeout('location.reload();', 100); +</script> +</head> +<body> +Testing location.reload(); +</body> +</html> diff --git a/toolkit/components/places/tests/browser/399606-location.replace.html b/toolkit/components/places/tests/browser/399606-location.replace.html new file mode 100644 index 000000000..36705402c --- /dev/null +++ b/toolkit/components/places/tests/browser/399606-location.replace.html @@ -0,0 +1,11 @@ +<html> +<head> +<title>location.replace</title> +<script> +setTimeout('location.replace(window.location.href)', 1000); +</script> +</head> +<body> +Testing location.replace +</body> +</html> diff --git a/toolkit/components/places/tests/browser/399606-window.location.href.html b/toolkit/components/places/tests/browser/399606-window.location.href.html new file mode 100644 index 000000000..61a2c8ba0 --- /dev/null +++ b/toolkit/components/places/tests/browser/399606-window.location.href.html @@ -0,0 +1,11 @@ +<html> +<head> +<title>window.location.href</title> +<script> +setTimeout('window.location.href = window.location.href', 1000); +</script> +</head> +<body> +Testing window.location.href +</body> +</html> diff --git a/toolkit/components/places/tests/browser/399606-window.location.html b/toolkit/components/places/tests/browser/399606-window.location.html new file mode 100644 index 000000000..e77f73071 --- /dev/null +++ b/toolkit/components/places/tests/browser/399606-window.location.html @@ -0,0 +1,11 @@ +<html> +<head> +<title>window.location</title> +<script> +setTimeout('window.location = window.location', 1000); +</script> +</head> +<body> +Testing window.location +</body> +</html> diff --git a/toolkit/components/places/tests/browser/461710_iframe.html b/toolkit/components/places/tests/browser/461710_iframe.html new file mode 100644 index 000000000..7480fe58f --- /dev/null +++ b/toolkit/components/places/tests/browser/461710_iframe.html @@ -0,0 +1,8 @@ +<!DOCTYPE HTML> +<html> + <head> + </head> + <body> + <iframe id="iframe"></iframe> + </body> +</html> diff --git a/toolkit/components/places/tests/browser/461710_link_page-2.html b/toolkit/components/places/tests/browser/461710_link_page-2.html new file mode 100644 index 000000000..1fc3e0959 --- /dev/null +++ b/toolkit/components/places/tests/browser/461710_link_page-2.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Link page 2</title>
+ <style type="text/css">
+ a:link { color: #0000ff; }
+ a:visited { color: #ff0000; }
+ </style>
+ </head>
+ <body>
+ <p><a href="461710_visited_page.html" id="link">Link to the second visited page</a></p>
+ </body>
+</html>
\ No newline at end of file diff --git a/toolkit/components/places/tests/browser/461710_link_page-3.html b/toolkit/components/places/tests/browser/461710_link_page-3.html new file mode 100644 index 000000000..596661803 --- /dev/null +++ b/toolkit/components/places/tests/browser/461710_link_page-3.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Link page 3</title>
+ <style type="text/css">
+ a:link { color: #0000ff; }
+ a:visited { color: #ff0000; }
+ </style>
+ </head>
+ <body>
+ <p><a href="461710_visited_page.html" id="link">Link to the third visited page</a></p>
+ </body>
+</html>
\ No newline at end of file diff --git a/toolkit/components/places/tests/browser/461710_link_page.html b/toolkit/components/places/tests/browser/461710_link_page.html new file mode 100644 index 000000000..6bea50628 --- /dev/null +++ b/toolkit/components/places/tests/browser/461710_link_page.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Link page</title>
+ <style type="text/css">
+ a:link { color: #0000ff; }
+ a:visited { color: #ff0000; }
+ </style>
+ </head>
+ <body>
+ <p><a href="461710_visited_page.html" id="link">Link to the visited page</a></p>
+ </body>
+</html>
\ No newline at end of file diff --git a/toolkit/components/places/tests/browser/461710_visited_page.html b/toolkit/components/places/tests/browser/461710_visited_page.html new file mode 100644 index 000000000..90e65116b --- /dev/null +++ b/toolkit/components/places/tests/browser/461710_visited_page.html @@ -0,0 +1,9 @@ +<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Visited page</title>
+ </head>
+ <body>
+ <p>This page is marked as visited</p>
+ </body>
+</html>
\ No newline at end of file diff --git a/toolkit/components/places/tests/browser/begin.html b/toolkit/components/places/tests/browser/begin.html new file mode 100644 index 000000000..da4c16dd2 --- /dev/null +++ b/toolkit/components/places/tests/browser/begin.html @@ -0,0 +1,10 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<html> + <body> + <a id="clickme" href="redirect_twice.sjs">Redirect twice</a> + </body> +</html> diff --git a/toolkit/components/places/tests/browser/browser.ini b/toolkit/components/places/tests/browser/browser.ini new file mode 100644 index 000000000..e6abe987f --- /dev/null +++ b/toolkit/components/places/tests/browser/browser.ini @@ -0,0 +1,26 @@ +[DEFAULT] +support-files = + colorAnalyzer/category-discover.png + colorAnalyzer/dictionaryGeneric-16.png + colorAnalyzer/extensionGeneric-16.png + colorAnalyzer/localeGeneric.png + head.js + +[browser_bug248970.js] +[browser_bug399606.js] +[browser_bug461710.js] +[browser_bug646422.js] +[browser_bug680727.js] +[browser_colorAnalyzer.js] +[browser_double_redirect.js] +[browser_favicon_privatebrowsing_perwindowpb.js] +[browser_favicon_setAndFetchFaviconForPage.js] +[browser_favicon_setAndFetchFaviconForPage_failures.js] +[browser_history_post.js] +[browser_notfound.js] +[browser_redirect.js] +[browser_settitle.js] +[browser_visited_notfound.js] +[browser_visituri.js] +[browser_visituri_nohistory.js] +[browser_visituri_privatebrowsing_perwindowpb.js]
\ No newline at end of file diff --git a/toolkit/components/places/tests/browser/browser_bug248970.js b/toolkit/components/places/tests/browser/browser_bug248970.js new file mode 100644 index 000000000..5850a3038 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_bug248970.js @@ -0,0 +1,152 @@ +/* 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 performs checks on the history testing area as outlined +// https://wiki.mozilla.org/Firefox3.1/PrivateBrowsing/TestPlan#History +// http://developer.mozilla.org/en/Using_the_Places_history_service + +var visitedURIs = [ + "http://www.test-link.com/", + "http://www.test-typed.com/", + "http://www.test-bookmark.com/", + "http://www.test-redirect-permanent.com/", + "http://www.test-redirect-temporary.com/", + "http://www.test-embed.com/", + "http://www.test-framed.com/", + "http://www.test-download.com/" +].map(NetUtil.newURI.bind(NetUtil)); + +add_task(function* () { + let windowsToClose = []; + let placeItemsCount = 0; + + registerCleanupFunction(function() { + windowsToClose.forEach(function(win) { + win.close(); + }); + }); + + yield PlacesTestUtils.clearHistory(); + + // Ensure we wait for the default bookmarks import. + yield new Promise(resolve => { + waitForCondition(() => { + placeItemsCount = getPlacesItemsCount(); + return placeItemsCount > 0 + }, resolve, "Should have default bookmarks") + }); + + // Create a handful of history items with various visit types + yield PlacesTestUtils.addVisits([ + { uri: visitedURIs[0], transition: TRANSITION_LINK }, + { uri: visitedURIs[1], transition: TRANSITION_TYPED }, + { uri: visitedURIs[2], transition: TRANSITION_BOOKMARK }, + { uri: visitedURIs[3], transition: TRANSITION_REDIRECT_PERMANENT }, + { uri: visitedURIs[4], transition: TRANSITION_REDIRECT_TEMPORARY }, + { uri: visitedURIs[5], transition: TRANSITION_EMBED }, + { uri: visitedURIs[6], transition: TRANSITION_FRAMED_LINK }, + { uri: visitedURIs[7], transition: TRANSITION_DOWNLOAD } + ]); + + placeItemsCount += 7; + // We added 7 new items to history. + is(getPlacesItemsCount(), placeItemsCount, + "Check the total items count"); + + function* testOnWindow(aIsPrivate, aCount) { + let win = yield new Promise(resolve => { + whenNewWindowLoaded({ private: aIsPrivate }, resolve); + }); + windowsToClose.push(win); + + // History items should be retrievable by query + yield checkHistoryItems(); + + // Updates the place items count + let count = getPlacesItemsCount(); + + // Create Bookmark + let title = "title " + windowsToClose.length; + let keyword = "keyword " + windowsToClose.length; + let url = "http://test-a-" + windowsToClose.length + ".com/"; + + yield PlacesUtils.bookmarks.insert({ url, title, + parentGuid: PlacesUtils.bookmarks.menuGuid }); + yield PlacesUtils.keywords.insert({ url, keyword }); + count++; + + ok((yield PlacesUtils.bookmarks.fetch({ url })), + "Bookmark should be bookmarked, data should be retrievable"); + is(getPlacesItemsCount(), count, + "Check the new bookmark items count"); + is(isBookmarkAltered(), false, "Check if bookmark has been visited"); + } + + // Test on windows. + yield testOnWindow(false); + yield testOnWindow(true); + yield testOnWindow(false); +}); + +/** + * Function performs a really simple query on our places entries, + * and makes sure that the number of entries equal num_places_entries. + */ +function getPlacesItemsCount() { + // Get bookmarks count + let options = PlacesUtils.history.getNewQueryOptions(); + options.includeHidden = true; + options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS; + let root = PlacesUtils.history.executeQuery( + PlacesUtils.history.getNewQuery(), options).root; + root.containerOpen = true; + let cc = root.childCount; + root.containerOpen = false; + + // Get history item count + options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY; + root = PlacesUtils.history.executeQuery( + PlacesUtils.history.getNewQuery(), options).root; + root.containerOpen = true; + cc += root.childCount; + root.containerOpen = false; + + return cc; +} + +function* checkHistoryItems() { + for (let i = 0; i < visitedURIs.length; i++) { + let visitedUri = visitedURIs[i]; + ok((yield promiseIsURIVisited(visitedUri)), ""); + if (/embed/.test(visitedUri.spec)) { + is((yield PlacesTestUtils.isPageInDB(visitedUri)), false, "Check if URI is in database"); + } else { + ok((yield PlacesTestUtils.isPageInDB(visitedUri)), "Check if URI is in database"); + } + } +} + +/** + * Function attempts to check if Bookmark-A has been visited + * during private browsing mode, function should return false + * + * @returns false if the accessCount has not changed + * true if the accessCount has changed + */ +function isBookmarkAltered() { + let options = PlacesUtils.history.getNewQueryOptions(); + options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS; + options.maxResults = 1; // should only expect a new bookmark + + let query = PlacesUtils.history.getNewQuery(); + query.setFolders([PlacesUtils.bookmarks.bookmarksMenuFolder], 1); + + let root = PlacesUtils.history.executeQuery(query, options).root; + root.containerOpen = true; + is(root.childCount, options.maxResults, "Check new bookmarks results"); + let node = root.getChild(0); + root.containerOpen = false; + + return (node.accessCount != 0); +} diff --git a/toolkit/components/places/tests/browser/browser_bug399606.js b/toolkit/components/places/tests/browser/browser_bug399606.js new file mode 100644 index 000000000..b5eee0f92 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_bug399606.js @@ -0,0 +1,77 @@ +/* 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/. */ + +gBrowser.selectedTab = gBrowser.addTab(); + +function test() { + waitForExplicitFinish(); + + var URIs = [ + "http://example.com/tests/toolkit/components/places/tests/browser/399606-window.location.href.html", + "http://example.com/tests/toolkit/components/places/tests/browser/399606-history.go-0.html", + "http://example.com/tests/toolkit/components/places/tests/browser/399606-location.replace.html", + "http://example.com/tests/toolkit/components/places/tests/browser/399606-location.reload.html", + "http://example.com/tests/toolkit/components/places/tests/browser/399606-httprefresh.html", + "http://example.com/tests/toolkit/components/places/tests/browser/399606-window.location.html", + ]; + var hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); + + // Create and add history observer. + var historyObserver = { + visitCount: Array(), + onBeginUpdateBatch: function () {}, + onEndUpdateBatch: function () {}, + onVisit: function (aURI, aVisitID, aTime, aSessionID, aReferringID, + aTransitionType) { + info("Received onVisit: " + aURI.spec); + if (aURI.spec in this.visitCount) + this.visitCount[aURI.spec]++; + else + this.visitCount[aURI.spec] = 1; + }, + onTitleChanged: function () {}, + onDeleteURI: function () {}, + onClearHistory: function () {}, + onPageChanged: function () {}, + onDeleteVisits: function () {}, + QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver]) + }; + hs.addObserver(historyObserver, false); + + function confirm_results() { + gBrowser.removeCurrentTab(); + hs.removeObserver(historyObserver, false); + for (let aURI in historyObserver.visitCount) { + is(historyObserver.visitCount[aURI], 1, + "onVisit has been received right number of times for " + aURI); + } + PlacesTestUtils.clearHistory().then(finish); + } + + var loadCount = 0; + function handleLoad(aEvent) { + loadCount++; + info("new load count is " + loadCount); + + if (loadCount == 3) { + gBrowser.removeEventListener("DOMContentLoaded", handleLoad, true); + gBrowser.loadURI("about:blank"); + executeSoon(check_next_uri); + } + } + + function check_next_uri() { + if (URIs.length) { + let uri = URIs.shift(); + loadCount = 0; + gBrowser.addEventListener("DOMContentLoaded", handleLoad, true); + gBrowser.loadURI(uri); + } + else { + confirm_results(); + } + } + executeSoon(check_next_uri); +} diff --git a/toolkit/components/places/tests/browser/browser_bug461710.js b/toolkit/components/places/tests/browser/browser_bug461710.js new file mode 100644 index 000000000..12af87a06 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_bug461710.js @@ -0,0 +1,82 @@ +const kRed = "rgb(255, 0, 0)"; +const kBlue = "rgb(0, 0, 255)"; + +const prefix = "http://example.com/tests/toolkit/components/places/tests/browser/461710_"; + +add_task(function* () { + let contentPage = prefix + "iframe.html"; + let normalWindow = yield BrowserTestUtils.openNewBrowserWindow(); + + let browser = normalWindow.gBrowser.selectedBrowser; + BrowserTestUtils.loadURI(browser, contentPage); + yield BrowserTestUtils.browserLoaded(browser, contentPage); + + let privateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true}); + + browser = privateWindow.gBrowser.selectedBrowser; + BrowserTestUtils.loadURI(browser, contentPage); + yield BrowserTestUtils.browserLoaded(browser, contentPage); + + let tests = [{ + win: normalWindow, + topic: "uri-visit-saved", + subtest: "visited_page.html" + }, { + win: normalWindow, + topic: "visited-status-resolution", + subtest: "link_page.html", + color: kRed, + message: "Visited link coloring should work outside of private mode" + }, { + win: privateWindow, + topic: "visited-status-resolution", + subtest: "link_page-2.html", + color: kBlue, + message: "Visited link coloring should not work inside of private mode" + }, { + win: normalWindow, + topic: "visited-status-resolution", + subtest: "link_page-3.html", + color: kRed, + message: "Visited link coloring should work outside of private mode" + }]; + + let visited_page_url = prefix + tests[0].subtest; + for (let test of tests) { + let promise = new Promise(resolve => { + let uri = NetUtil.newURI(visited_page_url); + Services.obs.addObserver(function observe(aSubject) { + if (uri.equals(aSubject.QueryInterface(Ci.nsIURI))) { + Services.obs.removeObserver(observe, test.topic); + resolve(); + } + }, test.topic, false); + }); + ContentTask.spawn(test.win.gBrowser.selectedBrowser, prefix + test.subtest, function* (aSrc) { + content.document.getElementById("iframe").src = aSrc; + }); + yield promise; + + if (test.color) { + // In e10s waiting for visited-status-resolution is not enough to ensure links + // have been updated, because it only tells us that messages to update links + // have been dispatched. We must still wait for the actual links to update. + yield BrowserTestUtils.waitForCondition(function* () { + let color = yield ContentTask.spawn(test.win.gBrowser.selectedBrowser, null, function* () { + let iframe = content.document.getElementById("iframe"); + let elem = iframe.contentDocument.getElementById("link"); + return content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .getVisitedDependentComputedStyle(elem, "", "color"); + }); + return (color == test.color); + }, test.message); + // The harness will consider the test as failed overall if there were no + // passes or failures, so record it as a pass. + ok(true, test.message); + } + } + + yield BrowserTestUtils.closeWindow(normalWindow); + yield BrowserTestUtils.closeWindow(privateWindow); +}); diff --git a/toolkit/components/places/tests/browser/browser_bug646422.js b/toolkit/components/places/tests/browser/browser_bug646422.js new file mode 100644 index 000000000..1a81de4e1 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_bug646422.js @@ -0,0 +1,51 @@ +/* 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/. */ + +/** + * Test for Bug 646224. Make sure that after changing the URI via + * history.pushState, the history service has a title stored for the new URI. + **/ + +add_task(function* () { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, 'http://example.com'); + + let newTitlePromise = new Promise(resolve => { + let observer = { + onTitleChanged: function(uri, title) { + // If the uri of the page whose title is changing ends with 'new_page', + // then it's the result of our pushState. + if (/new_page$/.test(uri.spec)) { + resolve(title); + PlacesUtils.history.removeObserver(observer); + } + }, + + onBeginUpdateBatch: function() { }, + onEndUpdateBatch: function() { }, + onVisit: function() { }, + onDeleteURI: function() { }, + onClearHistory: function() { }, + onPageChanged: function() { }, + onDeleteVisits: function() { }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver]) + }; + + PlacesUtils.history.addObserver(observer, false); + }); + + yield ContentTask.spawn(tab.linkedBrowser, null, function* () { + let title = content.document.title; + content.history.pushState('', '', 'new_page'); + Assert.ok(title, "Content window should initially have a title."); + }); + + let newtitle = yield newTitlePromise; + + yield ContentTask.spawn(tab.linkedBrowser, { newtitle }, function* (args) { + Assert.equal(args.newtitle, content.document.title, "Title after pushstate."); + }); + + yield PlacesTestUtils.clearHistory(); + gBrowser.removeTab(tab); +}); diff --git a/toolkit/components/places/tests/browser/browser_bug680727.js b/toolkit/components/places/tests/browser/browser_bug680727.js new file mode 100644 index 000000000..560cbfe6c --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_bug680727.js @@ -0,0 +1,109 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* Ensure that clicking the button in the Offline mode neterror page updates + global history. See bug 680727. */ +/* TEST_PATH=toolkit/components/places/tests/browser/browser_bug680727.js make -C $(OBJDIR) mochitest-browser-chrome */ + + +const kUniqueURI = Services.io.newURI("http://mochi.test:8888/#bug_680727", + null, null); +var gAsyncHistory = + Cc["@mozilla.org/browser/history;1"].getService(Ci.mozIAsyncHistory); + +var proxyPrefValue; +var ourTab; + +function test() { + waitForExplicitFinish(); + + // Tests always connect to localhost, and per bug 87717, localhost is now + // reachable in offline mode. To avoid this, disable any proxy. + proxyPrefValue = Services.prefs.getIntPref("network.proxy.type"); + Services.prefs.setIntPref("network.proxy.type", 0); + + // Clear network cache. + Components.classes["@mozilla.org/netwerk/cache-storage-service;1"] + .getService(Components.interfaces.nsICacheStorageService) + .clear(); + + // Go offline, expecting the error page. + Services.io.offline = true; + + BrowserTestUtils.openNewForegroundTab(gBrowser).then(tab => { + ourTab = tab; + BrowserTestUtils.waitForContentEvent(ourTab.linkedBrowser, "DOMContentLoaded") + .then(errorListener); + BrowserTestUtils.loadURI(ourTab.linkedBrowser, kUniqueURI.spec); + }); +} + +// ------------------------------------------------------------------------------ +// listen to loading the neterror page. (offline mode) +function errorListener() { + ok(Services.io.offline, "Services.io.offline is true."); + + // This is an error page. + ContentTask.spawn(ourTab.linkedBrowser, kUniqueURI.spec, function(uri) { + Assert.equal(content.document.documentURI.substring(0, 27), + "about:neterror?e=netOffline", "Document URI is the error page."); + + // But location bar should show the original request. + Assert.equal(content.location.href, uri, "Docshell URI is the original URI."); + }).then(() => { + // Global history does not record URI of a failed request. + return PlacesTestUtils.promiseAsyncUpdates().then(() => { + gAsyncHistory.isURIVisited(kUniqueURI, errorAsyncListener); + }); + }); +} + +function errorAsyncListener(aURI, aIsVisited) { + ok(kUniqueURI.equals(aURI) && !aIsVisited, + "The neterror page is not listed in global history."); + + Services.prefs.setIntPref("network.proxy.type", proxyPrefValue); + + // Now press the "Try Again" button, with offline mode off. + Services.io.offline = false; + + BrowserTestUtils.waitForContentEvent(ourTab.linkedBrowser, "DOMContentLoaded") + .then(reloadListener); + + ContentTask.spawn(ourTab.linkedBrowser, null, function() { + Assert.ok(content.document.getElementById("errorTryAgain"), + "The error page has got a #errorTryAgain element"); + content.document.getElementById("errorTryAgain").click(); + }); +} + +// ------------------------------------------------------------------------------ +// listen to reload of neterror. +function reloadListener() { + // This listener catches "DOMContentLoaded" on being called + // nsIWPL::onLocationChange(...). That is right *AFTER* + // IHistory::VisitURI(...) is called. + ok(!Services.io.offline, "Services.io.offline is false."); + + ContentTask.spawn(ourTab.linkedBrowser, kUniqueURI.spec, function(uri) { + // This is not an error page. + Assert.equal(content.document.documentURI, uri, + "Document URI is not the offline-error page, but the original URI."); + }).then(() => { + // Check if global history remembers the successfully-requested URI. + PlacesTestUtils.promiseAsyncUpdates().then(() => { + gAsyncHistory.isURIVisited(kUniqueURI, reloadAsyncListener); + }); + }); +} + +function reloadAsyncListener(aURI, aIsVisited) { + ok(kUniqueURI.equals(aURI) && aIsVisited, "We have visited the URI."); + PlacesTestUtils.clearHistory().then(finish); +} + +registerCleanupFunction(function* () { + Services.prefs.setIntPref("network.proxy.type", proxyPrefValue); + Services.io.offline = false; + yield BrowserTestUtils.removeTab(ourTab); +}); diff --git a/toolkit/components/places/tests/browser/browser_colorAnalyzer.js b/toolkit/components/places/tests/browser/browser_colorAnalyzer.js new file mode 100644 index 000000000..7b7fe6ec5 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_colorAnalyzer.js @@ -0,0 +1,259 @@ +/* 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/. */ + +"use strict"; + +Cu.import("resource://gre/modules/Services.jsm"); + +const CA = Cc["@mozilla.org/places/colorAnalyzer;1"]. + getService(Ci.mozIColorAnalyzer); + +const hiddenWindowDoc = Cc["@mozilla.org/appshell/appShellService;1"]. + getService(Ci.nsIAppShellService). + hiddenDOMWindow.document; + +const XHTML_NS = "http://www.w3.org/1999/xhtml"; + +/** + * Passes the given uri to findRepresentativeColor. + * If expected is null, you expect it to fail. + * If expected is a function, it will call that function. + * If expected is a color, you expect that color to be returned. + * Message is used in the calls to is(). + */ +function frcTest(uri, expected, message) { + return new Promise(resolve => { + CA.findRepresentativeColor(Services.io.newURI(uri, "", null), + function(success, color) { + if (expected == null) { + ok(!success, message); + } else if (typeof expected == "function") { + expected(color, message); + } else { + ok(success, "success: " + message); + is(color, expected, message); + } + resolve(); + }); + }); +} + +/** + * Handy function for getting an image into findRepresentativeColor and testing it. + * Makes a canvas with the given dimensions, calls paintCanvasFunc with the 2d + * context of the canvas, sticks the generated canvas into findRepresentativeColor. + * See frcTest. + */ +function canvasTest(width, height, paintCanvasFunc, expected, message) { + let canvas = hiddenWindowDoc.createElementNS(XHTML_NS, "canvas"); + canvas.width = width; + canvas.height = height; + paintCanvasFunc(canvas.getContext("2d")); + let uri = canvas.toDataURL(); + return frcTest(uri, expected, message); +} + +// simple test - draw a red box in the center, make sure we get red back +add_task(function* test_redSquare() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "red"; + ctx.fillRect(2, 2, 12, 12); + }, 0xFF0000, "redSquare analysis returns red"); +}); + + +// draw a blue square in one corner, red in the other, such that blue overlaps +// red by one pixel, making it the dominant color +add_task(function* test_blueOverlappingRed() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 8, 8); + ctx.fillStyle = "blue"; + ctx.fillRect(7, 7, 8, 8); + }, 0x0000FF, "blueOverlappingRed analysis returns blue"); +}); + +// draw a red gradient next to a solid blue rectangle to ensure that a large +// block of similar colors beats out a smaller block of one color +add_task(function* test_redGradientBlueSolid() { + yield canvasTest(16, 16, function(ctx) { + let gradient = ctx.createLinearGradient(0, 0, 1, 15); + gradient.addColorStop(0, "#FF0000"); + gradient.addColorStop(1, "#FF0808"); + + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, 16, 16); + ctx.fillStyle = "blue"; + ctx.fillRect(9, 0, 7, 16); + }, function(actual, message) { + ok(actual >= 0xFF0000 && actual <= 0xFF0808, message); + }, "redGradientBlueSolid analysis returns redish"); +}); + +// try a transparent image, should fail +add_task(function* test_transparent() { + yield canvasTest(16, 16, function(ctx) { + // do nothing! + }, null, "transparent analysis fails"); +}); + +add_task(function* test_invalidURI() { + yield frcTest("data:blah,Imnotavaliddatauri", null, "invalid URI analysis fails"); +}); + +add_task(function* test_malformedPNGURI() { + yield frcTest("data:image/png;base64,iVBORblahblahblah", null, + "malformed PNG URI analysis fails"); +}); + +add_task(function* test_unresolvableURI() { + yield frcTest("http://www.example.com/blah/idontexist.png", null, + "unresolvable URI analysis fails"); +}); + +// draw a small blue box on a red background to make sure the algorithm avoids +// using the background color +add_task(function* test_blueOnRedBackground() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 16, 16); + ctx.fillStyle = "blue"; + ctx.fillRect(4, 4, 8, 8); + }, 0x0000FF, "blueOnRedBackground analysis returns blue"); +}); + +// draw a slightly different color in the corners to make sure the corner colors +// don't have to be exactly equal to be considered the background color +add_task(function* test_variableBackground() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 16, 16); + ctx.fillStyle = "#FEFEFE"; + ctx.fillRect(15, 0, 1, 1); + ctx.fillStyle = "#FDFDFD"; + ctx.fillRect(15, 15, 1, 1); + ctx.fillStyle = "#FCFCFC"; + ctx.fillRect(0, 15, 1, 1); + ctx.fillStyle = "black"; + ctx.fillRect(4, 4, 8, 8); + }, 0x000000, "variableBackground analysis returns black"); +}); + +// like the above test, but make the colors different enough that they aren't +// considered the background color +add_task(function* test_tooVariableBackground() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 16, 16); + ctx.fillStyle = "#EEDDCC"; + ctx.fillRect(15, 0, 1, 1); + ctx.fillStyle = "#DDDDDD"; + ctx.fillRect(15, 15, 1, 1); + ctx.fillStyle = "#CCCCCC"; + ctx.fillRect(0, 15, 1, 1); + ctx.fillStyle = "black"; + ctx.fillRect(4, 4, 8, 8); + }, function(actual, message) { + isnot(actual, 0x000000, message); + }, "tooVariableBackground analysis doesn't return black"); +}); + +// draw a small black/white box over transparent background to make sure the +// algorithm doesn't think rgb(0,0,0) == rgba(0,0,0,0) +add_task(function* test_transparentBackgroundConflation() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "black"; + ctx.fillRect(2, 2, 12, 12); + ctx.fillStyle = "white"; + ctx.fillRect(5, 5, 6, 6); + }, 0x000000, "transparentBackgroundConflation analysis returns black"); +}); + + +// make sure we fall back to the background color if we have no other choice +// (instead of failing as if there were no colors) +add_task(function* test_backgroundFallback() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "black"; + ctx.fillRect(0, 0, 16, 16); + }, 0x000000, "backgroundFallback analysis returns black"); +}); + +// draw red rectangle next to a pink one to make sure the algorithm picks the +// more interesting color +add_task(function* test_interestingColorPreference() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "#FFDDDD"; + ctx.fillRect(0, 0, 16, 16); + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 3, 16); + }, 0xFF0000, "interestingColorPreference analysis returns red"); +}); + +// draw high saturation but dark red next to slightly less saturated color but +// much lighter, to make sure the algorithm doesn't pick colors that are +// nearly black just because of high saturation (in HSL terms) +add_task(function* test_saturationDependence() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "hsl(0, 100%, 5%)"; + ctx.fillRect(0, 0, 16, 16); + ctx.fillStyle = "hsl(0, 90%, 35%)"; + ctx.fillRect(0, 0, 8, 16); + }, 0xA90808, "saturationDependence analysis returns lighter red"); +}); + +// make sure the preference for interesting colors won't stupidly pick 1 pixel +// of red over 169 black pixels +add_task(function* test_interestingColorPreferenceLenient() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "black"; + ctx.fillRect(1, 1, 13, 13); + ctx.fillStyle = "red"; + ctx.fillRect(3, 3, 1, 1); + }, 0x000000, "interestingColorPreferenceLenient analysis returns black"); +}); + +// ...but 6 pixels of red is more reasonable +add_task(function* test_interestingColorPreferenceNotTooLenient() { + yield canvasTest(16, 16, function(ctx) { + ctx.fillStyle = "black"; + ctx.fillRect(1, 1, 13, 13); + ctx.fillStyle = "red"; + ctx.fillRect(3, 3, 3, 2); + }, 0xFF0000, "interestingColorPreferenceNotTooLenient analysis returns red"); +}); + +var maxPixels = 144; // see ColorAnalyzer MAXIMUM_PIXELS const + +// make sure that images larger than maxPixels*maxPixels fail +add_task(function* test_imageTooLarge() { + yield canvasTest(1+maxPixels, 1+maxPixels, function(ctx) { + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 1+maxPixels, 1+maxPixels); + }, null, "imageTooLarge analysis fails"); +}); + +// the rest of the tests are for coverage of "real" favicons +// exact color isn't terribly important, just make sure it's reasonable +const filePrefix = getRootDirectory(gTestPath) + "colorAnalyzer/"; + +add_task(function* test_categoryDiscover() { + yield frcTest(filePrefix + "category-discover.png", 0xB28D3A, + "category-discover analysis returns red"); +}); + +add_task(function* test_localeGeneric() { + yield frcTest(filePrefix + "localeGeneric.png", 0x3EC23E, + "localeGeneric analysis returns green"); +}); + +add_task(function* test_dictionaryGeneric() { + yield frcTest(filePrefix + "dictionaryGeneric-16.png", 0x854C30, + "dictionaryGeneric-16 analysis returns brown"); +}); + +add_task(function* test_extensionGeneric() { + yield frcTest(filePrefix + "extensionGeneric-16.png", 0x53BA3F, + "extensionGeneric-16 analysis returns green"); +}); diff --git a/toolkit/components/places/tests/browser/browser_double_redirect.js b/toolkit/components/places/tests/browser/browser_double_redirect.js new file mode 100644 index 000000000..1e5dc9c16 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_double_redirect.js @@ -0,0 +1,63 @@ +// Test for bug 411966. +// When a page redirects multiple times, from_visit should point to the +// previous visit in the chain, not to the first visit in the chain. + +add_task(function* () { + yield PlacesTestUtils.clearHistory(); + + const BASE_URL = "http://example.com/tests/toolkit/components/places/tests/browser/"; + const TEST_URI = NetUtil.newURI(BASE_URL + "begin.html"); + const FIRST_REDIRECTING_URI = NetUtil.newURI(BASE_URL + "redirect_twice.sjs"); + const FINAL_URI = NetUtil.newURI(BASE_URL + "final.html"); + + let promiseVisits = new Promise(resolve => { + PlacesUtils.history.addObserver({ + __proto__: NavHistoryObserver.prototype, + _notified: [], + onVisit: function (uri, id, time, sessionId, referrerId, transition) { + info("Received onVisit: " + uri.spec); + this._notified.push(uri); + + if (!uri.equals(FINAL_URI)) { + return; + } + + is(this._notified.length, 4); + PlacesUtils.history.removeObserver(this); + + Task.spawn(function* () { + // Get all pages visited from the original typed one + let db = yield PlacesUtils.promiseDBConnection(); + let rows = yield db.execute( + `SELECT url FROM moz_historyvisits + JOIN moz_places h ON h.id = place_id + WHERE from_visit IN + (SELECT v.id FROM moz_historyvisits v + JOIN moz_places p ON p.id = v.place_id + WHERE p.url_hash = hash(:url) AND p.url = :url) + `, { url: TEST_URI.spec }); + + is(rows.length, 1, "Found right number of visits"); + let visitedUrl = rows[0].getResultByName("url"); + // Check that redirect from_visit is not from the original typed one + is(visitedUrl, FIRST_REDIRECTING_URI.spec, "Check referrer for " + visitedUrl); + + resolve(); + }); + } + }, false); + }); + + PlacesUtils.history.markPageAsTyped(TEST_URI); + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: TEST_URI.spec, + }, function* (browser) { + // Load begin page, click link on page to record visits. + yield BrowserTestUtils.synthesizeMouseAtCenter("#clickme", {}, browser); + + yield promiseVisits; + }); + + yield PlacesTestUtils.clearHistory(); +}); diff --git a/toolkit/components/places/tests/browser/browser_favicon_privatebrowsing_perwindowpb.js b/toolkit/components/places/tests/browser/browser_favicon_privatebrowsing_perwindowpb.js new file mode 100644 index 000000000..51d82adc6 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_favicon_privatebrowsing_perwindowpb.js @@ -0,0 +1,43 @@ +/* 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/. */ + +function test() { + waitForExplicitFinish(); + + const pageURI = + "http://example.org/tests/toolkit/components/places/tests/browser/favicon.html"; + let windowsToClose = []; + + registerCleanupFunction(function() { + windowsToClose.forEach(function(aWin) { + aWin.close(); + }); + }); + + function testOnWindow(aIsPrivate, aCallback) { + whenNewWindowLoaded({private: aIsPrivate}, function(aWin) { + windowsToClose.push(aWin); + executeSoon(() => aCallback(aWin)); + }); + } + + function waitForTabLoad(aWin, aCallback) { + aWin.gBrowser.selectedBrowser.addEventListener("load", function onLoad() { + aWin.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true); + aCallback(); + }, true); + aWin.gBrowser.selectedBrowser.loadURI(pageURI); + } + + testOnWindow(true, function(win) { + waitForTabLoad(win, function() { + PlacesUtils.favicons.getFaviconURLForPage(NetUtil.newURI(pageURI), + function(uri, dataLen, data, mimeType) { + is(uri, null, "No result should be found"); + finish(); + } + ); + }); + }); +} diff --git a/toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage.js b/toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage.js new file mode 100644 index 000000000..60df8ebd7 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage.js @@ -0,0 +1,152 @@ +/* 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 file tests the normal operation of setAndFetchFaviconForPage. +function test() { + // Initialization + waitForExplicitFinish(); + let windowsToClose = []; + let favIconLocation = + "http://example.org/tests/toolkit/components/places/tests/browser/favicon-normal32.png"; + let favIconURI = NetUtil.newURI(favIconLocation); + let favIconMimeType= "image/png"; + let pageURI; + let favIconData; + + function testOnWindow(aOptions, aCallback) { + whenNewWindowLoaded(aOptions, function(aWin) { + windowsToClose.push(aWin); + executeSoon(() => aCallback(aWin)); + }); + } + + // This function is called after calling finish() on the test. + registerCleanupFunction(function() { + windowsToClose.forEach(function(aWin) { + aWin.close(); + }); + }); + + function getIconFile(aCallback) { + NetUtil.asyncFetch({ + uri: favIconLocation, + loadUsingSystemPrincipal: true, + contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON + }, function(inputStream, status) { + if (!Components.isSuccessCode(status)) { + ok(false, "Could not get the icon file"); + // Handle error. + return; + } + + // Check the returned size versus the expected size. + let size = inputStream.available(); + favIconData = NetUtil.readInputStreamToString(inputStream, size); + is(size, favIconData.length, "Check correct icon size"); + // Check that the favicon loaded correctly before starting the actual tests. + is(favIconData.length, 344, "Check correct icon length (344)"); + + if (aCallback) { + aCallback(); + } else { + finish(); + } + }); + } + + function testNormal(aWindow, aCallback) { + pageURI = NetUtil.newURI("http://example.com/normal"); + waitForFaviconChanged(pageURI, favIconURI, aWindow, + function testNormalCallback() { + checkFaviconDataForPage(pageURI, favIconMimeType, favIconData, aWindow, + aCallback); + } + ); + + addVisits({uri: pageURI, transition: TRANSITION_TYPED}, aWindow, + function () { + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, favIconURI, + true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + } + ); + } + + function testAboutURIBookmarked(aWindow, aCallback) { + pageURI = NetUtil.newURI("about:testAboutURI_bookmarked"); + waitForFaviconChanged(pageURI, favIconURI, aWindow, + function testAboutURIBookmarkedCallback() { + checkFaviconDataForPage(pageURI, favIconMimeType, favIconData, aWindow, + aCallback); + } + ); + + aWindow.PlacesUtils.bookmarks.insertBookmark( + aWindow.PlacesUtils.unfiledBookmarksFolderId, pageURI, + aWindow.PlacesUtils.bookmarks.DEFAULT_INDEX, pageURI.spec); + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, favIconURI, + true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + } + + function testPrivateBrowsingBookmarked(aWindow, aCallback) { + pageURI = NetUtil.newURI("http://example.com/privateBrowsing_bookmarked"); + waitForFaviconChanged(pageURI, favIconURI, aWindow, + function testPrivateBrowsingBookmarkedCallback() { + checkFaviconDataForPage(pageURI, favIconMimeType, favIconData, aWindow, + aCallback); + } + ); + + aWindow.PlacesUtils.bookmarks.insertBookmark( + aWindow.PlacesUtils.unfiledBookmarksFolderId, pageURI, + aWindow.PlacesUtils.bookmarks.DEFAULT_INDEX, pageURI.spec); + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, favIconURI, + true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + } + + function testDisabledHistoryBookmarked(aWindow, aCallback) { + pageURI = NetUtil.newURI("http://example.com/disabledHistory_bookmarked"); + waitForFaviconChanged(pageURI, favIconURI, aWindow, + function testDisabledHistoryBookmarkedCallback() { + checkFaviconDataForPage(pageURI, favIconMimeType, favIconData, aWindow, + aCallback); + } + ); + + // Disable history while changing the favicon. + aWindow.Services.prefs.setBoolPref("places.history.enabled", false); + + aWindow.PlacesUtils.bookmarks.insertBookmark( + aWindow.PlacesUtils.unfiledBookmarksFolderId, pageURI, + aWindow.PlacesUtils.bookmarks.DEFAULT_INDEX, pageURI.spec); + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, favIconURI, + true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + + // The setAndFetchFaviconForPage function calls CanAddURI synchronously, thus + // we can set the preference back to true immediately. We don't clear the + // preference because not all products enable Places by default. + aWindow.Services.prefs.setBoolPref("places.history.enabled", true); + } + + getIconFile(function () { + testOnWindow({}, function(aWin) { + testNormal(aWin, function () { + testOnWindow({}, function(aWin2) { + testAboutURIBookmarked(aWin2, function () { + testOnWindow({private: true}, function(aWin3) { + testPrivateBrowsingBookmarked(aWin3, function () { + testOnWindow({}, function(aWin4) { + testDisabledHistoryBookmarked(aWin4, finish); + }); + }); + }); + }); + }); + }); + }); + }); +} diff --git a/toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage_failures.js b/toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage_failures.js new file mode 100644 index 000000000..bd73af441 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage_failures.js @@ -0,0 +1,261 @@ +/* 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 file tests setAndFetchFaviconForPage when it is called with invalid + * arguments, and when no favicon is stored for the given arguments. + */ +function test() { + // Initialization + waitForExplicitFinish(); + let windowsToClose = []; + let favIcon16Location = + "http://example.org/tests/toolkit/components/places/tests/browser/favicon-normal16.png"; + let favIcon32Location = + "http://example.org/tests/toolkit/components/places/tests/browser/favicon-normal32.png"; + let favIcon16URI = NetUtil.newURI(favIcon16Location); + let favIcon32URI = NetUtil.newURI(favIcon32Location); + let lastPageURI = NetUtil.newURI("http://example.com/verification"); + // This error icon must stay in sync with FAVICON_ERRORPAGE_URL in + // nsIFaviconService.idl, aboutCertError.xhtml and netError.xhtml. + let favIconErrorPageURI = + NetUtil.newURI("chrome://global/skin/icons/warning-16.png"); + let favIconsResultCount = 0; + + function testOnWindow(aOptions, aCallback) { + whenNewWindowLoaded(aOptions, function(aWin) { + windowsToClose.push(aWin); + executeSoon(() => aCallback(aWin)); + }); + } + + // This function is called after calling finish() on the test. + registerCleanupFunction(function() { + windowsToClose.forEach(function(aWin) { + aWin.close(); + }); + }); + + function checkFavIconsDBCount(aCallback) { + let stmt = DBConn().createAsyncStatement("SELECT url FROM moz_favicons"); + stmt.executeAsync({ + handleResult: function final_handleResult(aResultSet) { + while (aResultSet.getNextRow()) { + favIconsResultCount++; + } + }, + handleError: function final_handleError(aError) { + throw ("Unexpected error (" + aError.result + "): " + aError.message); + }, + handleCompletion: function final_handleCompletion(aReason) { + // begin testing + info("Previous records in moz_favicons: " + favIconsResultCount); + if (aCallback) { + aCallback(); + } + } + }); + stmt.finalize(); + } + + function testNullPageURI(aWindow, aCallback) { + try { + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(null, favIcon16URI, + true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + throw ("Exception expected because aPageURI is null."); + } catch (ex) { + // We expected an exception. + ok(true, "Exception expected because aPageURI is null"); + } + + if (aCallback) { + aCallback(); + } + } + + function testNullFavIconURI(aWindow, aCallback) { + try { + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage( + NetUtil.newURI("http://example.com/null_faviconURI"), null, + true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, + null, Services.scriptSecurityManager.getSystemPrincipal()); + throw ("Exception expected because aFaviconURI is null."); + } catch (ex) { + // We expected an exception. + ok(true, "Exception expected because aFaviconURI is null."); + } + + if (aCallback) { + aCallback(); + } + } + + function testAboutURI(aWindow, aCallback) { + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage( + NetUtil.newURI("about:testAboutURI"), favIcon16URI, + true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, + null, Services.scriptSecurityManager.getSystemPrincipal()); + + if (aCallback) { + aCallback(); + } + } + + function testPrivateBrowsingNonBookmarkedURI(aWindow, aCallback) { + let pageURI = NetUtil.newURI("http://example.com/privateBrowsing"); + addVisits({ uri: pageURI, transitionType: TRANSITION_TYPED }, aWindow, + function () { + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, + favIcon16URI, true, + aWindow.PlacesUtils.favicons.FAVICON_LOAD_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + + if (aCallback) { + aCallback(); + } + }); + } + + function testDisabledHistory(aWindow, aCallback) { + let pageURI = NetUtil.newURI("http://example.com/disabledHistory"); + addVisits({ uri: pageURI, transition: TRANSITION_TYPED }, aWindow, + function () { + aWindow.Services.prefs.setBoolPref("places.history.enabled", false); + + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, + favIcon16URI, true, + aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + + // The setAndFetchFaviconForPage function calls CanAddURI synchronously, thus + // we can set the preference back to true immediately . We don't clear the + // preference because not all products enable Places by default. + aWindow.Services.prefs.setBoolPref("places.history.enabled", true); + + if (aCallback) { + aCallback(); + } + }); + } + + function testErrorIcon(aWindow, aCallback) { + let pageURI = NetUtil.newURI("http://example.com/errorIcon"); + addVisits({ uri: pageURI, transition: TRANSITION_TYPED }, aWindow, + function () { + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, + favIconErrorPageURI, true, + aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + + if (aCallback) { + aCallback(); + } + }); + } + + function testNonExistingPage(aWindow, aCallback) { + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage( + NetUtil.newURI("http://example.com/nonexistingPage"), favIcon16URI, + true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + + if (aCallback) { + aCallback(); + } + } + + function testFinalVerification(aWindow, aCallback) { + // Only the last test should raise the onPageChanged notification, + // executing the waitForFaviconChanged callback. + waitForFaviconChanged(lastPageURI, favIcon32URI, aWindow, + function final_callback() { + // Check that only one record corresponding to the last favicon is present. + let resultCount = 0; + let stmt = DBConn().createAsyncStatement("SELECT url FROM moz_favicons"); + stmt.executeAsync({ + handleResult: function final_handleResult(aResultSet) { + + // If the moz_favicons DB had been previously loaded (before our + // test began), we should focus only in the URI we are testing and + // skip the URIs not related to our test. + if (favIconsResultCount > 0) { + for (let row; (row = aResultSet.getNextRow()); ) { + if (favIcon32URI.spec === row.getResultByIndex(0)) { + is(favIcon32URI.spec, row.getResultByIndex(0), + "Check equal favicons"); + resultCount++; + } + } + } else { + for (let row; (row = aResultSet.getNextRow()); ) { + is(favIcon32URI.spec, row.getResultByIndex(0), + "Check equal favicons"); + resultCount++; + } + } + }, + handleError: function final_handleError(aError) { + throw ("Unexpected error (" + aError.result + "): " + aError.message); + }, + handleCompletion: function final_handleCompletion(aReason) { + is(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason, + "Check reasons are equal"); + is(1, resultCount, "Check result count"); + if (aCallback) { + aCallback(); + } + } + }); + stmt.finalize(); + }); + + // This is the only test that should cause the waitForFaviconChanged + // callback to be invoked. In turn, the callback will invoke + // finish() causing the tests to finish. + addVisits({ uri: lastPageURI, transition: TRANSITION_TYPED }, aWindow, + function () { + aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(lastPageURI, + favIcon32URI, true, + aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, + Services.scriptSecurityManager.getSystemPrincipal()); + }); + } + + checkFavIconsDBCount(function () { + testOnWindow({}, function(aWin) { + testNullPageURI(aWin, function () { + testOnWindow({}, function(aWin2) { + testNullFavIconURI(aWin2, function() { + testOnWindow({}, function(aWin3) { + testAboutURI(aWin3, function() { + testOnWindow({private: true}, function(aWin4) { + testPrivateBrowsingNonBookmarkedURI(aWin4, function () { + testOnWindow({}, function(aWin5) { + testDisabledHistory(aWin5, function () { + testOnWindow({}, function(aWin6) { + testErrorIcon(aWin6, function() { + testOnWindow({}, function(aWin7) { + testNonExistingPage(aWin7, function() { + testOnWindow({}, function(aWin8) { + testFinalVerification(aWin8, function() { + finish(); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); +} diff --git a/toolkit/components/places/tests/browser/browser_history_post.js b/toolkit/components/places/tests/browser/browser_history_post.js new file mode 100644 index 000000000..c85e720f8 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_history_post.js @@ -0,0 +1,23 @@ +const PAGE_URI = "http://example.com/tests/toolkit/components/places/tests/browser/history_post.html"; +const SJS_URI = NetUtil.newURI("http://example.com/tests/toolkit/components/places/tests/browser/history_post.sjs"); + +add_task(function* () { + yield BrowserTestUtils.withNewTab({gBrowser, url: PAGE_URI}, Task.async(function* (aBrowser) { + yield ContentTask.spawn(aBrowser, null, function* () { + let doc = content.document; + let submit = doc.getElementById("submit"); + let iframe = doc.getElementById("post_iframe"); + let p = new Promise((resolve, reject) => { + iframe.addEventListener("load", function onLoad() { + iframe.removeEventListener("load", onLoad); + resolve(); + }); + }); + submit.click(); + yield p; + }); + let visited = yield promiseIsURIVisited(SJS_URI); + ok(!visited, "The POST page should not be added to history"); + ok(!(yield PlacesTestUtils.isPageInDB(SJS_URI.spec)), "The page should not be in the database"); + })); +}); diff --git a/toolkit/components/places/tests/browser/browser_notfound.js b/toolkit/components/places/tests/browser/browser_notfound.js new file mode 100644 index 000000000..20467eef4 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_notfound.js @@ -0,0 +1,46 @@ +/* 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/. */ + +add_task(function* () { + const TEST_URL = "http://mochi.test:8888/notFoundPage.html"; + + // Used to verify errors are not marked as typed. + PlacesUtils.history.markPageAsTyped(NetUtil.newURI(TEST_URL)); + + // Create and add history observer. + let visitedPromise = new Promise(resolve => { + let historyObserver = { + onVisit: function (aURI, aVisitID, aTime, aSessionID, aReferringID, + aTransitionType) { + PlacesUtils.history.removeObserver(historyObserver); + info("Received onVisit: " + aURI.spec); + fieldForUrl(aURI, "frecency", function (aFrecency) { + is(aFrecency, 0, "Frecency should be 0"); + fieldForUrl(aURI, "hidden", function (aHidden) { + is(aHidden, 0, "Page should not be hidden"); + fieldForUrl(aURI, "typed", function (aTyped) { + is(aTyped, 0, "page should not be marked as typed"); + resolve(); + }); + }); + }); + }, + onBeginUpdateBatch: function () {}, + onEndUpdateBatch: function () {}, + onTitleChanged: function () {}, + onDeleteURI: function () {}, + onClearHistory: function () {}, + onPageChanged: function () {}, + onDeleteVisits: function () {}, + QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver]) + }; + PlacesUtils.history.addObserver(historyObserver, false); + }); + + let newTabPromise = BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + yield Promise.all([visitedPromise, newTabPromise]); + + yield PlacesTestUtils.clearHistory(); + gBrowser.removeCurrentTab(); +}); diff --git a/toolkit/components/places/tests/browser/browser_redirect.js b/toolkit/components/places/tests/browser/browser_redirect.js new file mode 100644 index 000000000..d8a19731a --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_redirect.js @@ -0,0 +1,61 @@ +/* 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/. */ + +add_task(function* () { + const REDIRECT_URI = NetUtil.newURI("http://mochi.test:8888/tests/toolkit/components/places/tests/browser/redirect.sjs"); + const TARGET_URI = NetUtil.newURI("http://mochi.test:8888/tests/toolkit/components/places/tests/browser/redirect-target.html"); + + // Create and add history observer. + let visitedPromise = new Promise(resolve => { + let historyObserver = { + _redirectNotified: false, + onVisit: function (aURI, aVisitID, aTime, aSessionID, aReferringID, + aTransitionType) { + info("Received onVisit: " + aURI.spec); + + if (aURI.equals(REDIRECT_URI)) { + this._redirectNotified = true; + // Wait for the target page notification. + return; + } + + PlacesUtils.history.removeObserver(historyObserver); + + ok(this._redirectNotified, "The redirect should have been notified"); + + fieldForUrl(REDIRECT_URI, "frecency", function (aFrecency) { + ok(aFrecency != 0, "Frecency or the redirecting page should not be 0"); + + fieldForUrl(REDIRECT_URI, "hidden", function (aHidden) { + is(aHidden, 1, "The redirecting page should be hidden"); + + fieldForUrl(TARGET_URI, "frecency", function (aFrecency2) { + ok(aFrecency2 != 0, "Frecency of the target page should not be 0"); + + fieldForUrl(TARGET_URI, "hidden", function (aHidden2) { + is(aHidden2, 0, "The target page should not be hidden"); + resolve(); + }); + }); + }); + }); + }, + onBeginUpdateBatch: function () {}, + onEndUpdateBatch: function () {}, + onTitleChanged: function () {}, + onDeleteURI: function () {}, + onClearHistory: function () {}, + onPageChanged: function () {}, + onDeleteVisits: function () {}, + QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver]) + }; + PlacesUtils.history.addObserver(historyObserver, false); + }); + + let newTabPromise = BrowserTestUtils.openNewForegroundTab(gBrowser, REDIRECT_URI.spec); + yield Promise.all([visitedPromise, newTabPromise]); + + yield PlacesTestUtils.clearHistory(); + gBrowser.removeCurrentTab(); +}); diff --git a/toolkit/components/places/tests/browser/browser_settitle.js b/toolkit/components/places/tests/browser/browser_settitle.js new file mode 100644 index 000000000..68c8deda7 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_settitle.js @@ -0,0 +1,76 @@ +var conn = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection; + +/** + * Gets a single column value from either the places or historyvisits table. + */ +function getColumn(table, column, url) +{ + var stmt = conn.createStatement( + `SELECT ${column} FROM ${table} WHERE url_hash = hash(:val) AND url = :val`); + try { + stmt.params.val = url; + stmt.executeStep(); + return stmt.row[column]; + } + finally { + stmt.finalize(); + } +} + +add_task(function* () +{ + // Make sure titles are correctly saved for a URI with the proper + // notifications. + + // Create and add history observer. + let titleChangedPromise = new Promise(resolve => { + var historyObserver = { + data: [], + onBeginUpdateBatch: function() {}, + onEndUpdateBatch: function() {}, + onVisit: function(aURI, aVisitID, aTime, aSessionID, aReferringID, + aTransitionType) { + }, + onTitleChanged: function(aURI, aPageTitle, aGUID) { + this.data.push({ uri: aURI, title: aPageTitle, guid: aGUID }); + + // We only expect one title change. + // + // Although we are loading two different pages, the first page does not + // have a title. Since the title starts out as empty and then is set + // to empty, there is no title change notification. + + PlacesUtils.history.removeObserver(this); + resolve(this.data); + }, + onDeleteURI: function() {}, + onClearHistory: function() {}, + onPageChanged: function() {}, + onDeleteVisits: function() {}, + QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver]) + }; + PlacesUtils.history.addObserver(historyObserver, false); + }); + + const url1 = "http://example.com/tests/toolkit/components/places/tests/browser/title1.html"; + yield BrowserTestUtils.openNewForegroundTab(gBrowser, url1); + + const url2 = "http://example.com/tests/toolkit/components/places/tests/browser/title2.html"; + let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url2); + yield loadPromise; + + let data = yield titleChangedPromise; + is(data[0].uri.spec, "http://example.com/tests/toolkit/components/places/tests/browser/title2.html"); + is(data[0].title, "Some title"); + is(data[0].guid, getColumn("moz_places", "guid", data[0].uri.spec)); + + data.forEach(function(item) { + var title = getColumn("moz_places", "title", data[0].uri.spec); + is(title, item.title); + }); + + gBrowser.removeCurrentTab(); + yield PlacesTestUtils.clearHistory(); +}); + diff --git a/toolkit/components/places/tests/browser/browser_visited_notfound.js b/toolkit/components/places/tests/browser/browser_visited_notfound.js new file mode 100644 index 000000000..b2b4f25b8 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_visited_notfound.js @@ -0,0 +1,51 @@ +/* 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/. */ + +const TEST_URI = NetUtil.newURI("http://mochi.test:8888/notFoundPage.html"); + +function test() { + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + registerCleanupFunction(function() { + gBrowser.removeCurrentTab(); + }); + + // First add a visit to the page, this will ensure that later we skip + // updating the frecency for a newly not-found page. + addVisits({ uri: TEST_URI }, window, () => { + info("Added visit"); + fieldForUrl(TEST_URI, "frecency", aFrecency => { + ok(aFrecency > 0, "Frecency should be > 0"); + continueTest(aFrecency); + }); + }); +} + +function continueTest(aOldFrecency) { + // Used to verify errors are not marked as typed. + PlacesUtils.history.markPageAsTyped(TEST_URI); + gBrowser.selectedBrowser.loadURI(TEST_URI.spec); + + // Create and add history observer. + let historyObserver = { + __proto__: NavHistoryObserver.prototype, + onVisit: function (aURI, aVisitID, aTime, aSessionID, aReferringID, + aTransitionType) { + PlacesUtils.history.removeObserver(historyObserver); + info("Received onVisit: " + aURI.spec); + fieldForUrl(aURI, "frecency", function (aFrecency) { + is(aFrecency, aOldFrecency, "Frecency should be unchanged"); + fieldForUrl(aURI, "hidden", function (aHidden) { + is(aHidden, 0, "Page should not be hidden"); + fieldForUrl(aURI, "typed", function (aTyped) { + is(aTyped, 0, "page should not be marked as typed"); + PlacesTestUtils.clearHistory().then(finish); + }); + }); + }); + } + }; + PlacesUtils.history.addObserver(historyObserver, false); +} diff --git a/toolkit/components/places/tests/browser/browser_visituri.js b/toolkit/components/places/tests/browser/browser_visituri.js new file mode 100644 index 000000000..8ba2b7272 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_visituri.js @@ -0,0 +1,84 @@ +/** + * One-time observer callback. + */ +function promiseObserve(name, checkFn) { + return new Promise(resolve => { + Services.obs.addObserver(function observer(subject) { + if (checkFn(subject)) { + Services.obs.removeObserver(observer, name); + resolve(); + } + }, name, false); + }); +} + +var conn = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection; + +/** + * Gets a single column value from either the places or historyvisits table. + */ +function getColumn(table, column, fromColumnName, fromColumnValue) { + let sql = `SELECT ${column} + FROM ${table} + WHERE ${fromColumnName} = :val + ${fromColumnName == "url" ? "AND url_hash = hash(:val)" : ""} + LIMIT 1`; + let stmt = conn.createStatement(sql); + try { + stmt.params.val = fromColumnValue; + ok(stmt.executeStep(), "Expect to get a row"); + return stmt.row[column]; + } + finally { + stmt.reset(); + } +} + +add_task(function* () { + // Make sure places visit chains are saved correctly with a redirect + // transitions. + + // Part 1: observe history events that fire when a visit occurs. + // Make sure visits appear in order, and that the visit chain is correct. + var expectedUrls = [ + "http://example.com/tests/toolkit/components/places/tests/browser/begin.html", + "http://example.com/tests/toolkit/components/places/tests/browser/redirect_twice.sjs", + "http://example.com/tests/toolkit/components/places/tests/browser/redirect_once.sjs", + "http://example.com/tests/toolkit/components/places/tests/browser/final.html" + ]; + var currentIndex = 0; + + function checkObserver(subject) { + var uri = subject.QueryInterface(Ci.nsIURI); + var expected = expectedUrls[currentIndex]; + is(uri.spec, expected, "Saved URL visit " + uri.spec); + + var placeId = getColumn("moz_places", "id", "url", uri.spec); + var fromVisitId = getColumn("moz_historyvisits", "from_visit", "place_id", placeId); + + if (currentIndex == 0) { + is(fromVisitId, 0, "First visit has no from visit"); + } + else { + var lastVisitId = getColumn("moz_historyvisits", "place_id", "id", fromVisitId); + var fromVisitUrl = getColumn("moz_places", "url", "id", lastVisitId); + is(fromVisitUrl, expectedUrls[currentIndex - 1], + "From visit was " + expectedUrls[currentIndex - 1]); + } + + currentIndex++; + return (currentIndex >= expectedUrls.length); + } + let visitUriPromise = promiseObserve("uri-visit-saved", checkObserver); + + const testUrl = "http://example.com/tests/toolkit/components/places/tests/browser/begin.html"; + yield BrowserTestUtils.openNewForegroundTab(gBrowser, testUrl); + + // Load begin page, click link on page to record visits. + yield BrowserTestUtils.synthesizeMouseAtCenter("#clickme", { }, gBrowser.selectedBrowser); + yield visitUriPromise; + + yield PlacesTestUtils.clearHistory(); + + gBrowser.removeCurrentTab(); +}); diff --git a/toolkit/components/places/tests/browser/browser_visituri_nohistory.js b/toolkit/components/places/tests/browser/browser_visituri_nohistory.js new file mode 100644 index 000000000..a3a8e7626 --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_visituri_nohistory.js @@ -0,0 +1,42 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const INITIAL_URL = "http://example.com/tests/toolkit/components/places/tests/browser/begin.html"; +const FINAL_URL = "http://example.com/tests/toolkit/components/places/tests/browser/final.html"; + +/** + * One-time observer callback. + */ +function promiseObserve(name) +{ + return new Promise(resolve => { + Services.obs.addObserver(function observer(subject) { + Services.obs.removeObserver(observer, name); + resolve(subject); + }, name, false); + }); +} + +add_task(function* () +{ + yield new Promise(resolve => SpecialPowers.pushPrefEnv({"set": [["places.history.enabled", false]]}, resolve)); + + let visitUriPromise = promiseObserve("uri-visit-saved"); + + yield BrowserTestUtils.openNewForegroundTab(gBrowser, INITIAL_URL); + + yield new Promise(resolve => SpecialPowers.popPrefEnv(resolve)); + + let browserLoadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + gBrowser.loadURI(FINAL_URL); + yield browserLoadedPromise; + + let subject = yield visitUriPromise; + let uri = subject.QueryInterface(Ci.nsIURI); + is(uri.spec, FINAL_URL, "received expected visit"); + + yield PlacesTestUtils.clearHistory(); + gBrowser.removeCurrentTab(); +}); diff --git a/toolkit/components/places/tests/browser/browser_visituri_privatebrowsing_perwindowpb.js b/toolkit/components/places/tests/browser/browser_visituri_privatebrowsing_perwindowpb.js new file mode 100644 index 000000000..abde69a7d --- /dev/null +++ b/toolkit/components/places/tests/browser/browser_visituri_privatebrowsing_perwindowpb.js @@ -0,0 +1,73 @@ +/* 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/. */ + +function test() { + // initialization + waitForExplicitFinish(); + let windowsToClose = []; + let initialURL = + "http://example.com/tests/toolkit/components/places/tests/browser/begin.html"; + let finalURL = + "http://example.com/tests/toolkit/components/places/tests/browser/final.html"; + let observer = null; + let enumerator = null; + let currentObserver = null; + let uri = null; + + function doTest(aIsPrivateMode, aWindow, aTestURI, aCallback) { + observer = { + observe: function(aSubject, aTopic, aData) { + // The uri-visit-saved topic should only work when on normal mode. + if (aTopic == "uri-visit-saved") { + // Remove the observers set on per window private mode and normal + // mode. + enumerator = aWindow.Services.obs.enumerateObservers("uri-visit-saved"); + while (enumerator.hasMoreElements()) { + currentObserver = enumerator.getNext(); + aWindow.Services.obs.removeObserver(currentObserver, "uri-visit-saved"); + } + + // The expected visit should be the finalURL because private mode + // should not register a visit with the initialURL. + uri = aSubject.QueryInterface(Ci.nsIURI); + is(uri.spec, finalURL, "Check received expected visit"); + } + } + }; + + aWindow.Services.obs.addObserver(observer, "uri-visit-saved", false); + + BrowserTestUtils.browserLoaded(aWindow.gBrowser.selectedBrowser).then(aCallback); + aWindow.gBrowser.selectedBrowser.loadURI(aTestURI); + } + + function testOnWindow(aOptions, aCallback) { + whenNewWindowLoaded(aOptions, function(aWin) { + windowsToClose.push(aWin); + // execute should only be called when need, like when you are opening + // web pages on the test. If calling executeSoon() is not necesary, then + // call whenNewWindowLoaded() instead of testOnWindow() on your test. + executeSoon(() => aCallback(aWin)); + }); + } + + // This function is called after calling finish() on the test. + registerCleanupFunction(function() { + windowsToClose.forEach(function(aWin) { + aWin.close(); + }); + }); + + // test first when on private mode + testOnWindow({private: true}, function(aWin) { + doTest(true, aWin, initialURL, function() { + // then test when not on private mode + testOnWindow({}, function(aWin2) { + doTest(false, aWin2, finalURL, function () { + PlacesTestUtils.clearHistory().then(finish); + }); + }); + }); + }); +} diff --git a/toolkit/components/places/tests/browser/colorAnalyzer/category-discover.png b/toolkit/components/places/tests/browser/colorAnalyzer/category-discover.png Binary files differnew file mode 100644 index 000000000..a6f5b49b3 --- /dev/null +++ b/toolkit/components/places/tests/browser/colorAnalyzer/category-discover.png diff --git a/toolkit/components/places/tests/browser/colorAnalyzer/dictionaryGeneric-16.png b/toolkit/components/places/tests/browser/colorAnalyzer/dictionaryGeneric-16.png Binary files differnew file mode 100644 index 000000000..4ad1a1a82 --- /dev/null +++ b/toolkit/components/places/tests/browser/colorAnalyzer/dictionaryGeneric-16.png diff --git a/toolkit/components/places/tests/browser/colorAnalyzer/extensionGeneric-16.png b/toolkit/components/places/tests/browser/colorAnalyzer/extensionGeneric-16.png Binary files differnew file mode 100644 index 000000000..fc6c8a258 --- /dev/null +++ b/toolkit/components/places/tests/browser/colorAnalyzer/extensionGeneric-16.png diff --git a/toolkit/components/places/tests/browser/colorAnalyzer/localeGeneric.png b/toolkit/components/places/tests/browser/colorAnalyzer/localeGeneric.png Binary files differnew file mode 100644 index 000000000..4d9ac5ad8 --- /dev/null +++ b/toolkit/components/places/tests/browser/colorAnalyzer/localeGeneric.png diff --git a/toolkit/components/places/tests/browser/favicon-normal16.png b/toolkit/components/places/tests/browser/favicon-normal16.png Binary files differnew file mode 100644 index 000000000..62b69a3d0 --- /dev/null +++ b/toolkit/components/places/tests/browser/favicon-normal16.png diff --git a/toolkit/components/places/tests/browser/favicon-normal32.png b/toolkit/components/places/tests/browser/favicon-normal32.png Binary files differnew file mode 100644 index 000000000..5535363c9 --- /dev/null +++ b/toolkit/components/places/tests/browser/favicon-normal32.png diff --git a/toolkit/components/places/tests/browser/favicon.html b/toolkit/components/places/tests/browser/favicon.html new file mode 100644 index 000000000..a0f5ea959 --- /dev/null +++ b/toolkit/components/places/tests/browser/favicon.html @@ -0,0 +1,13 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<html> + <head> + <link rel="shortcut icon" href="http://example.org/tests/toolkit/components/places/tests/browser/favicon-normal32.png"> + </head> + <body> + OK we're done! + </body> +</html> diff --git a/toolkit/components/places/tests/browser/final.html b/toolkit/components/places/tests/browser/final.html new file mode 100644 index 000000000..ccd581918 --- /dev/null +++ b/toolkit/components/places/tests/browser/final.html @@ -0,0 +1,10 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<html> + <body> + OK we're done! + </body> +</html> diff --git a/toolkit/components/places/tests/browser/head.js b/toolkit/components/places/tests/browser/head.js new file mode 100644 index 000000000..897585a81 --- /dev/null +++ b/toolkit/components/places/tests/browser/head.js @@ -0,0 +1,319 @@ +Components.utils.import("resource://gre/modules/PlacesUtils.jsm"); +Components.utils.import("resource://gre/modules/NetUtil.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "BrowserTestUtils", + "resource://testing-common/BrowserTestUtils.jsm"); + +const TRANSITION_LINK = PlacesUtils.history.TRANSITION_LINK; +const TRANSITION_TYPED = PlacesUtils.history.TRANSITION_TYPED; +const TRANSITION_BOOKMARK = PlacesUtils.history.TRANSITION_BOOKMARK; +const TRANSITION_REDIRECT_PERMANENT = PlacesUtils.history.TRANSITION_REDIRECT_PERMANENT; +const TRANSITION_REDIRECT_TEMPORARY = PlacesUtils.history.TRANSITION_REDIRECT_TEMPORARY; +const TRANSITION_EMBED = PlacesUtils.history.TRANSITION_EMBED; +const TRANSITION_FRAMED_LINK = PlacesUtils.history.TRANSITION_FRAMED_LINK; +const TRANSITION_DOWNLOAD = PlacesUtils.history.TRANSITION_DOWNLOAD; + +/** + * Returns a moz_places field value for a url. + * + * @param aURI + * The URI or spec to get field for. + * param aCallback + * Callback function that will get the property value. + */ +function fieldForUrl(aURI, aFieldName, aCallback) +{ + let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI; + let stmt = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase) + .DBConnection.createAsyncStatement( + `SELECT ${aFieldName} FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url` + ); + stmt.params.page_url = url; + stmt.executeAsync({ + _value: -1, + handleResult: function(aResultSet) { + let row = aResultSet.getNextRow(); + if (!row) + ok(false, "The page should exist in the database"); + this._value = row.getResultByName(aFieldName); + }, + handleError: function() {}, + handleCompletion: function(aReason) { + if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) + ok(false, "The statement should properly succeed"); + aCallback(this._value); + } + }); + stmt.finalize(); +} + +/** + * Generic nsINavHistoryObserver that doesn't implement anything, but provides + * dummy methods to prevent errors about an object not having a certain method. + */ +function NavHistoryObserver() {} + +NavHistoryObserver.prototype = { + onBeginUpdateBatch: function () {}, + onEndUpdateBatch: function () {}, + onVisit: function () {}, + onTitleChanged: function () {}, + onDeleteURI: function () {}, + onClearHistory: function () {}, + onPageChanged: function () {}, + onDeleteVisits: function () {}, + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsINavHistoryObserver, + ]) +}; + +/** + * 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, aWindow, + aCallback) { + let historyObserver = { + __proto__: NavHistoryObserver.prototype, + onPageChanged: function WFFC_onPageChanged(aURI, aWhat, aValue, aGUID) { + if (aWhat != Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) { + return; + } + aWindow.PlacesUtils.history.removeObserver(this); + + ok(aURI.equals(aExpectedPageURI), + "Check URIs are equal for the page which favicon changed"); + is(aValue, aExpectedFaviconURI.spec, + "Check changed favicon URI is the expected"); + checkGuidForURI(aURI, aGUID); + + if (aCallback) { + aCallback(); + } + } + }; + aWindow.PlacesUtils.history.addObserver(historyObserver, false); +} + +/** + * Asynchronously adds visits to a page, invoking a callback function when done. + * + * @param aPlaceInfo + * Either an nsIURI, in such a case a single LINK visit will be added. + * Or can be an object describing the visit to add, or an array + * of these objects: + * { uri: nsIURI of the page, + * transition: one of the TRANSITION_* from nsINavHistoryService, + * [optional] title: title of the page, + * [optional] visitDate: visit date in microseconds from the epoch + * [optional] referrer: nsIURI of the referrer for this visit + * } + * @param [optional] aCallback + * Function to be invoked on completion. + * @param [optional] aStack + * The stack frame used to report errors. + */ +function addVisits(aPlaceInfo, aWindow, aCallback, aStack) { + let places = []; + if (aPlaceInfo instanceof Ci.nsIURI) { + places.push({ uri: aPlaceInfo }); + } + else if (Array.isArray(aPlaceInfo)) { + places = places.concat(aPlaceInfo); + } else { + places.push(aPlaceInfo) + } + + // Create mozIVisitInfo for each entry. + let now = Date.now(); + for (let place of places) { + if (!place.title) { + place.title = "test visit for " + place.uri.spec; + } + place.visits = [{ + transitionType: place.transition === undefined ? TRANSITION_LINK + : place.transition, + visitDate: place.visitDate || (now++) * 1000, + referrerURI: place.referrer + }]; + } + + aWindow.PlacesUtils.asyncHistory.updatePlaces( + places, + { + handleError: function AAV_handleError() { + throw ("Unexpected error in adding visit."); + }, + handleResult: function () {}, + handleCompletion: function UP_handleCompletion() { + if (aCallback) + aCallback(); + } + } + ); +} + +/** + * 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, + aWindow, aCallback) { + aWindow.PlacesUtils.favicons.getFaviconDataForPage(aPageURI, + function (aURI, aDataLen, aData, aMimeType) { + is(aExpectedMimeType, aMimeType, "Check expected MimeType"); + is(aExpectedData.length, aData.length, + "Check favicon data for the given page matches the provided data"); + checkGuidForURI(aPageURI); + aCallback(); + }); +} + +/** + * Tests that a guid was set in moz_places for a given uri. + * + * @param aURI + * The uri to check. + * @param [optional] aGUID + * The expected guid in the database. + */ +function checkGuidForURI(aURI, aGUID) { + let guid = doGetGuidForURI(aURI); + if (aGUID) { + doCheckValidPlacesGuid(aGUID); + is(guid, aGUID, "Check equal guid for URIs"); + } +} + +/** + * Retrieves the guid for a given uri. + * + * @param aURI + * The uri to check. + * @return the associated the guid. + */ +function doGetGuidForURI(aURI) { + let stmt = DBConn().createStatement( + `SELECT guid + FROM moz_places + WHERE url_hash = hash(:url) AND url = :url` + ); + stmt.params.url = aURI.spec; + ok(stmt.executeStep(), "Check get guid for uri from moz_places"); + let guid = stmt.row.guid; + stmt.finalize(); + doCheckValidPlacesGuid(guid); + return guid; +} + +/** + * Tests if a given guid is valid for use in Places or not. + * + * @param aGuid + * The guid to test. + */ +function doCheckValidPlacesGuid(aGuid) { + ok(/^[a-zA-Z0-9\-_]{12}$/.test(aGuid), "Check guid for valid places"); +} + +/** + * Gets the database connection. If the Places connection is invalid it will + * try to create a new connection. + * + * @param [optional] aForceNewConnection + * Forces creation of a new connection to the database. When a + * connection is asyncClosed it cannot anymore schedule async statements, + * though connectionReady will keep returning true (Bug 726990). + * + * @return The database connection or null if unable to get one. + */ +function DBConn(aForceNewConnection) { + let gDBConn; + if (!aForceNewConnection) { + let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase) + .DBConnection; + if (db.connectionReady) + return db; + } + + // If the Places database connection has been closed, create a new connection. + if (!gDBConn || aForceNewConnection) { + let file = Services.dirsvc.get('ProfD', Ci.nsIFile); + file.append("places.sqlite"); + let dbConn = gDBConn = Services.storage.openDatabase(file); + + // Be sure to cleanly close this connection. + Services.obs.addObserver(function DBCloseCallback(aSubject, aTopic, aData) { + Services.obs.removeObserver(DBCloseCallback, aTopic); + dbConn.asyncClose(); + }, "profile-before-change", false); + } + + return gDBConn.connectionReady ? gDBConn : null; +} + +function whenNewWindowLoaded(aOptions, aCallback) { + BrowserTestUtils.waitForNewWindow().then(aCallback); + OpenBrowserWindow(aOptions); +} + +/** + * Asynchronously check a url is visited. + * + * @param aURI The URI. + * @param aExpectedValue The expected value. + * @return {Promise} + * @resolves When the check has been added successfully. + * @rejects JavaScript exception. + */ +function promiseIsURIVisited(aURI, aExpectedValue) { + return new Promise(resolve => { + PlacesUtils.asyncHistory.isURIVisited(aURI, function(unused, aIsVisited) { + resolve(aIsVisited); + }); + }); +} + +function waitForCondition(condition, nextTest, errorMsg) { + let tries = 0; + let interval = setInterval(function() { + if (tries >= 30) { + ok(false, errorMsg); + moveOn(); + } + let conditionPassed; + try { + conditionPassed = condition(); + } catch (e) { + ok(false, e + "\n" + e.stack); + conditionPassed = false; + } + if (conditionPassed) { + moveOn(); + } + tries++; + }, 200); + function moveOn() { + clearInterval(interval); + nextTest(); + } +} diff --git a/toolkit/components/places/tests/browser/history_post.html b/toolkit/components/places/tests/browser/history_post.html new file mode 100644 index 000000000..a579a9b8a --- /dev/null +++ b/toolkit/components/places/tests/browser/history_post.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Test post pages are not added to history</title> + </head> + <body> + <iframe name="post_iframe" id="post_iframe"></iframe> + <form method="post" action="http://example.com/tests/toolkit/components/places/tests/browser/history_post.sjs" target="post_iframe"> + <input type="submit" id="submit"/> + </form> + </body> +</html> diff --git a/toolkit/components/places/tests/browser/history_post.sjs b/toolkit/components/places/tests/browser/history_post.sjs new file mode 100644 index 000000000..3c86aad7b --- /dev/null +++ b/toolkit/components/places/tests/browser/history_post.sjs @@ -0,0 +1,6 @@ +function handleRequest(request, response) +{ + response.setStatusLine("1.0", 200, "OK"); + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + response.write("Ciao"); +} diff --git a/toolkit/components/places/tests/browser/redirect-target.html b/toolkit/components/places/tests/browser/redirect-target.html new file mode 100644 index 000000000..370026338 --- /dev/null +++ b/toolkit/components/places/tests/browser/redirect-target.html @@ -0,0 +1 @@ +<!DOCTYPE html><html><body><p>Ciao!</p></body></html> diff --git a/toolkit/components/places/tests/browser/redirect.sjs b/toolkit/components/places/tests/browser/redirect.sjs new file mode 100644 index 000000000..f55e78eb1 --- /dev/null +++ b/toolkit/components/places/tests/browser/redirect.sjs @@ -0,0 +1,14 @@ +/* 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/. */ + +function handleRequest(request, response) +{ + let page = "<!DOCTYPE html><html><body><p>Redirecting...</p></body></html>"; + + response.setStatusLine(request.httpVersion, "301", "Moved Permanently"); + response.setHeader("Content-Type", "text/html", false); + response.setHeader("Content-Length", page.length + "", false); + response.setHeader("Location", "redirect-target.html", false); + response.write(page); +} diff --git a/toolkit/components/places/tests/browser/redirect_once.sjs b/toolkit/components/places/tests/browser/redirect_once.sjs new file mode 100644 index 000000000..8b2a8aa55 --- /dev/null +++ b/toolkit/components/places/tests/browser/redirect_once.sjs @@ -0,0 +1,9 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function handleRequest(request, response) { + response.setStatusLine("1.1", 301, "Found"); + response.setHeader("Location", "final.html", false); +} diff --git a/toolkit/components/places/tests/browser/redirect_twice.sjs b/toolkit/components/places/tests/browser/redirect_twice.sjs new file mode 100644 index 000000000..099d20022 --- /dev/null +++ b/toolkit/components/places/tests/browser/redirect_twice.sjs @@ -0,0 +1,9 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function handleRequest(request, response) { + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", "redirect_once.sjs", false); +} diff --git a/toolkit/components/places/tests/browser/title1.html b/toolkit/components/places/tests/browser/title1.html new file mode 100644 index 000000000..3c98d693e --- /dev/null +++ b/toolkit/components/places/tests/browser/title1.html @@ -0,0 +1,12 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<html> + <head> + </head> + <body> + title1.html + </body> +</html> diff --git a/toolkit/components/places/tests/browser/title2.html b/toolkit/components/places/tests/browser/title2.html new file mode 100644 index 000000000..28a6b69b5 --- /dev/null +++ b/toolkit/components/places/tests/browser/title2.html @@ -0,0 +1,14 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<html> + <head> + <title>Some title</title> + </head> + <body> + title2.html + </body> +</html> + |