summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/tests/browser
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/places/tests/browser')
-rw-r--r--toolkit/components/places/tests/browser/.eslintrc.js8
-rw-r--r--toolkit/components/places/tests/browser/399606-history.go-0.html11
-rw-r--r--toolkit/components/places/tests/browser/399606-httprefresh.html8
-rw-r--r--toolkit/components/places/tests/browser/399606-location.reload.html11
-rw-r--r--toolkit/components/places/tests/browser/399606-location.replace.html11
-rw-r--r--toolkit/components/places/tests/browser/399606-window.location.href.html11
-rw-r--r--toolkit/components/places/tests/browser/399606-window.location.html11
-rw-r--r--toolkit/components/places/tests/browser/461710_iframe.html8
-rw-r--r--toolkit/components/places/tests/browser/461710_link_page-2.html13
-rw-r--r--toolkit/components/places/tests/browser/461710_link_page-3.html13
-rw-r--r--toolkit/components/places/tests/browser/461710_link_page.html13
-rw-r--r--toolkit/components/places/tests/browser/461710_visited_page.html9
-rw-r--r--toolkit/components/places/tests/browser/begin.html10
-rw-r--r--toolkit/components/places/tests/browser/browser.ini26
-rw-r--r--toolkit/components/places/tests/browser/browser_bug248970.js152
-rw-r--r--toolkit/components/places/tests/browser/browser_bug399606.js77
-rw-r--r--toolkit/components/places/tests/browser/browser_bug461710.js82
-rw-r--r--toolkit/components/places/tests/browser/browser_bug646422.js51
-rw-r--r--toolkit/components/places/tests/browser/browser_bug680727.js109
-rw-r--r--toolkit/components/places/tests/browser/browser_colorAnalyzer.js259
-rw-r--r--toolkit/components/places/tests/browser/browser_double_redirect.js63
-rw-r--r--toolkit/components/places/tests/browser/browser_favicon_privatebrowsing_perwindowpb.js43
-rw-r--r--toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage.js152
-rw-r--r--toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage_failures.js261
-rw-r--r--toolkit/components/places/tests/browser/browser_history_post.js23
-rw-r--r--toolkit/components/places/tests/browser/browser_notfound.js46
-rw-r--r--toolkit/components/places/tests/browser/browser_redirect.js61
-rw-r--r--toolkit/components/places/tests/browser/browser_settitle.js76
-rw-r--r--toolkit/components/places/tests/browser/browser_visited_notfound.js51
-rw-r--r--toolkit/components/places/tests/browser/browser_visituri.js84
-rw-r--r--toolkit/components/places/tests/browser/browser_visituri_nohistory.js42
-rw-r--r--toolkit/components/places/tests/browser/browser_visituri_privatebrowsing_perwindowpb.js73
-rw-r--r--toolkit/components/places/tests/browser/colorAnalyzer/category-discover.pngbin0 -> 1324 bytes
-rw-r--r--toolkit/components/places/tests/browser/colorAnalyzer/dictionaryGeneric-16.pngbin0 -> 742 bytes
-rw-r--r--toolkit/components/places/tests/browser/colorAnalyzer/extensionGeneric-16.pngbin0 -> 554 bytes
-rw-r--r--toolkit/components/places/tests/browser/colorAnalyzer/localeGeneric.pngbin0 -> 2410 bytes
-rw-r--r--toolkit/components/places/tests/browser/favicon-normal16.pngbin0 -> 286 bytes
-rw-r--r--toolkit/components/places/tests/browser/favicon-normal32.pngbin0 -> 344 bytes
-rw-r--r--toolkit/components/places/tests/browser/favicon.html13
-rw-r--r--toolkit/components/places/tests/browser/final.html10
-rw-r--r--toolkit/components/places/tests/browser/head.js319
-rw-r--r--toolkit/components/places/tests/browser/history_post.html12
-rw-r--r--toolkit/components/places/tests/browser/history_post.sjs6
-rw-r--r--toolkit/components/places/tests/browser/redirect-target.html1
-rw-r--r--toolkit/components/places/tests/browser/redirect.sjs14
-rw-r--r--toolkit/components/places/tests/browser/redirect_once.sjs9
-rw-r--r--toolkit/components/places/tests/browser/redirect_twice.sjs9
-rw-r--r--toolkit/components/places/tests/browser/title1.html12
-rw-r--r--toolkit/components/places/tests/browser/title2.html14
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
new file mode 100644
index 000000000..a6f5b49b3
--- /dev/null
+++ b/toolkit/components/places/tests/browser/colorAnalyzer/category-discover.png
Binary files differ
diff --git a/toolkit/components/places/tests/browser/colorAnalyzer/dictionaryGeneric-16.png b/toolkit/components/places/tests/browser/colorAnalyzer/dictionaryGeneric-16.png
new file mode 100644
index 000000000..4ad1a1a82
--- /dev/null
+++ b/toolkit/components/places/tests/browser/colorAnalyzer/dictionaryGeneric-16.png
Binary files differ
diff --git a/toolkit/components/places/tests/browser/colorAnalyzer/extensionGeneric-16.png b/toolkit/components/places/tests/browser/colorAnalyzer/extensionGeneric-16.png
new file mode 100644
index 000000000..fc6c8a258
--- /dev/null
+++ b/toolkit/components/places/tests/browser/colorAnalyzer/extensionGeneric-16.png
Binary files differ
diff --git a/toolkit/components/places/tests/browser/colorAnalyzer/localeGeneric.png b/toolkit/components/places/tests/browser/colorAnalyzer/localeGeneric.png
new file mode 100644
index 000000000..4d9ac5ad8
--- /dev/null
+++ b/toolkit/components/places/tests/browser/colorAnalyzer/localeGeneric.png
Binary files differ
diff --git a/toolkit/components/places/tests/browser/favicon-normal16.png b/toolkit/components/places/tests/browser/favicon-normal16.png
new file mode 100644
index 000000000..62b69a3d0
--- /dev/null
+++ b/toolkit/components/places/tests/browser/favicon-normal16.png
Binary files differ
diff --git a/toolkit/components/places/tests/browser/favicon-normal32.png b/toolkit/components/places/tests/browser/favicon-normal32.png
new file mode 100644
index 000000000..5535363c9
--- /dev/null
+++ b/toolkit/components/places/tests/browser/favicon-normal32.png
Binary files differ
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>
+