diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/suite/browser/test | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
55 files changed, 5038 insertions, 0 deletions
diff --git a/comm/suite/browser/test/browser/alltabslistener.html b/comm/suite/browser/test/browser/alltabslistener.html new file mode 100644 index 0000000000..166c31037a --- /dev/null +++ b/comm/suite/browser/test/browser/alltabslistener.html @@ -0,0 +1,8 @@ +<html> +<head> +<title>Test page for bug 463387</title> +</head> +<body> +<p>Test page for bug 463387</p> +</body> +</html> diff --git a/comm/suite/browser/test/browser/authenticate.sjs b/comm/suite/browser/test/browser/authenticate.sjs new file mode 100644 index 0000000000..2f8c85adc1 --- /dev/null +++ b/comm/suite/browser/test/browser/authenticate.sjs @@ -0,0 +1,210 @@ +function handleRequest(request, response) +{ + try { + reallyHandleRequest(request, response); + } catch (e) { + response.setStatusLine("1.0", 200, "AlmostOK"); + response.write("Error handling request: " + e); + } +} + + +function reallyHandleRequest(request, response) { + var match; + var requestAuth = true, requestProxyAuth = true; + + // Allow the caller to drive how authentication is processed via the query. + // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar + // The extra ? allows the user/pass/realm checks to succeed if the name is + // at the beginning of the query string. + var query = "?" + request.queryString; + + var expected_user = "", expected_pass = "", realm = "mochitest"; + var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; + var huge = false, anonymous = false; + var authHeaderCount = 1; + // user=xxx + match = /[^_]user=([^&]*)/.exec(query); + if (match) + expected_user = match[1]; + + // pass=xxx + match = /[^_]pass=([^&]*)/.exec(query); + if (match) + expected_pass = match[1]; + + // realm=xxx + match = /[^_]realm=([^&]*)/.exec(query); + if (match) + realm = match[1]; + + // proxy_user=xxx + match = /proxy_user=([^&]*)/.exec(query); + if (match) + proxy_expected_user = match[1]; + + // proxy_pass=xxx + match = /proxy_pass=([^&]*)/.exec(query); + if (match) + proxy_expected_pass = match[1]; + + // proxy_realm=xxx + match = /proxy_realm=([^&]*)/.exec(query); + if (match) + proxy_realm = match[1]; + + // huge=1 + match = /huge=1/.exec(query); + if (match) + huge = true; + + // multiple=1 + match = /multiple=([^&]*)/.exec(query); + if (match) + authHeaderCount = match[1]+0; + + // anonymous=1 + match = /anonymous=1/.exec(query); + if (match) + anonymous = true; + + // Look for an authentication header, if any, in the request. + // + // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + // + // This test only supports Basic auth. The value sent by the client is + // "username:password", obscured with base64 encoding. + + var actual_user = "", actual_pass = "", authHeader, authPresent = false; + if (request.hasHeader("Authorization")) { + authPresent = true; + authHeader = request.getHeader("Authorization"); + match = /Basic (.+)/.exec(authHeader); + if (match.length != 2) + throw "Couldn't parse auth header: " + authHeader; + + var userpass = base64ToString(match[1]); // no atob() :-( + match = /(.*):(.*)/.exec(userpass); + if (match.length != 3) + throw "Couldn't decode auth header: " + userpass; + actual_user = match[1]; + actual_pass = match[2]; + } + + var proxy_actual_user = "", proxy_actual_pass = ""; + if (request.hasHeader("Proxy-Authorization")) { + authHeader = request.getHeader("Proxy-Authorization"); + match = /Basic (.+)/.exec(authHeader); + if (match.length != 2) + throw "Couldn't parse auth header: " + authHeader; + + var userpass = base64ToString(match[1]); // no atob() :-( + match = /(.*):(.*)/.exec(userpass); + if (match.length != 3) + throw "Couldn't decode auth header: " + userpass; + proxy_actual_user = match[1]; + proxy_actual_pass = match[2]; + } + + // Don't request authentication if the credentials we got were what we + // expected. + if (expected_user == actual_user && + expected_pass == actual_pass) { + requestAuth = false; + } + if (proxy_expected_user == proxy_actual_user && + proxy_expected_pass == proxy_actual_pass) { + requestProxyAuth = false; + } + + if (anonymous) { + if (authPresent) { + response.setStatusLine("1.0", 400, "Unexpected authorization header found"); + } else { + response.setStatusLine("1.0", 200, "Authorization header not found"); + } + } else { + if (requestProxyAuth) { + response.setStatusLine("1.0", 407, "Proxy authentication required"); + for (i = 0; i < authHeaderCount; ++i) + response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); + } else if (requestAuth) { + response.setStatusLine("1.0", 401, "Authentication required"); + for (i = 0; i < authHeaderCount; ++i) + response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); + } else { + response.setStatusLine("1.0", 200, "OK"); + } + } + + response.setHeader("Content-Type", "application/xhtml+xml", false); + response.write("<html xmlns='http://www.w3.org/1999/xhtml'>"); + response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n"); + response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n"); + response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n"); + response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n"); + response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n"); + + if (huge) { + response.write("<div style='display: none'>"); + for (i = 0; i < 100000; i++) { + response.write("123456789\n"); + } + response.write("</div>"); + response.write("<span id='footnote'>This is a footnote after the huge content fill</span>"); + } + + response.write("</html>"); +} + + +// base64 decoder +// +// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() +// doesn't seem to exist. :-( +/* Convert Base64 data to a string */ +const toBinaryTable = [ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +]; +const base64Pad = '='; + +function base64ToString(data) { + + var result = ''; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = (data[i] == base64Pad); + // Skip illegal characters and whitespace + if (c == -1) continue; + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) + throw Components.Exception('Corrupted base64 string'); + + return result; +} diff --git a/comm/suite/browser/test/browser/browser.ini b/comm/suite/browser/test/browser/browser.ini new file mode 100644 index 0000000000..a6b00157c6 --- /dev/null +++ b/comm/suite/browser/test/browser/browser.ini @@ -0,0 +1,38 @@ +[DEFAULT] +support-files = + head.js + +[browser_alltabslistener.js] +support-files = alltabslistener.html +[browser_bug329212.js] +support-files = title_test.svg +[browser_bug409624.js] +[browser_bug413915.js] +[browser_bug427559.js] +[browser_bug435325.js] +[browser_bug462289.js] +skip-if = toolkit == "cocoa" +[browser_bug519216.js] +[browser_bug561636.js] +[browser_bug562649.js] +[browser_bug581947.js] +[browser_bug585511.js] +[browser_bug595507.js] +[browser_bug623155.js] +support-files = redirect_bug623155.sjs +[browser_fayt.js] +[browser_page_style_menu.js] +support-files = page_style_sample.html +[browser_notification_tab_switching.js] +support-files = file_dom_notifications.html +[browser_pageInfo.js] +support-files = feed_tab.html +[browser_popupNotification.js] +[browser_privatebrowsing_protocolhandler.js] +support-files = browser_privatebrowsing_protocolhandler_page.html +[browser_relatedTabs.js] +[browser_scope.js] +[browser_selectTabAtIndex.js] +[browser_urlbarCopying.js] +support-files = + authenticate.sjs diff --git a/comm/suite/browser/test/browser/browser_alltabslistener.js b/comm/suite/browser/test/browser/browser_alltabslistener.js new file mode 100644 index 0000000000..609a4e66e8 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_alltabslistener.js @@ -0,0 +1,201 @@ +const gCompleteState = Ci.nsIWebProgressListener.STATE_STOP + + Ci.nsIWebProgressListener.STATE_IS_NETWORK; + +var gFrontProgressListener = { + onProgressChange: function (aWebProgress, aRequest, + aCurSelfProgress, aMaxSelfProgress, + aCurTotalProgress, aMaxTotalProgress) { + }, + + onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) { + var state = "onStateChange"; + info("FrontProgress: " + state + " 0x" + aStateFlags.toString(16)); + ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener"); + is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener"); + gFrontNotificationsPos++; + }, + + onLocationChange: function (aWebProgress, aRequest, aLocationURI, aFlags) { + var state = "onLocationChange"; + info("FrontProgress: " + state + " " + aLocationURI.spec); + ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener"); + is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener"); + gFrontNotificationsPos++; + }, + + onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) { + }, + + onSecurityChange: function (aWebProgress, aRequest, aState) { + var state = "onSecurityChange"; + info("FrontProgress: " + state + " 0x" + aState.toString(16)); + ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener"); + is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener"); + gFrontNotificationsPos++; + } +} + +var gAllProgressListener = { + onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { + var state = "onStateChange"; + info("AllProgress: " + state + " 0x" + aStateFlags.toString(16)); + is(aBrowser, gTestBrowser, state + " notification came from the correct browser"); + ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener"); + is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener"); + gAllNotificationsPos++; + + if ((aStateFlags & gCompleteState) == gCompleteState) { + is(gAllNotificationsPos, gAllNotifications.length, "Saw the expected number of notifications"); + is(gFrontNotificationsPos, gFrontNotifications.length, "Saw the expected number of frontnotifications"); + executeSoon(gNextTest); + } + }, + + onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI, aFlags) { + var state = "onLocationChange"; + info("AllProgress: " + state + " " + aLocationURI.spec); + is(aBrowser, gTestBrowser, state + " notification came from the correct browser"); + ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener"); + is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener"); + gAllNotificationsPos++; + }, + + onStatusChange: function (aBrowser, aWebProgress, aRequest, aStatus, aMessage) { + var state = "onStatusChange"; + is(aBrowser, gTestBrowser, state + " notification came from the correct browser"); + }, + + onSecurityChange: function (aBrowser, aWebProgress, aRequest, aState) { + var state = "onSecurityChange"; + info("AllProgress: " + state + " 0x" + aState.toString(16)); + is(aBrowser, gTestBrowser, state + " notification came from the correct browser"); + ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener"); + is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener"); + gAllNotificationsPos++; + } +} + +var gFrontNotifications, gAllNotifications, gFrontNotificationsPos, gAllNotificationsPos; +var gBackgroundTab, gForegroundTab, gBackgroundBrowser, gForegroundBrowser, gTestBrowser; +var gTestPage = "/browser/suite/browser/test/browser/alltabslistener.html"; +var gNextTest; + +function test() { + waitForExplicitFinish(); + + gBackgroundTab = getBrowser().addTab("about:blank"); + gForegroundTab = getBrowser().addTab("about:blank"); + gBackgroundBrowser = getBrowser().getBrowserForTab(gBackgroundTab); + gForegroundBrowser = getBrowser().getBrowserForTab(gForegroundTab); + getBrowser().selectedTab = gForegroundTab; + + // We must wait until the about:blank page has completed loading before + // starting tests or we get notifications from that + gForegroundBrowser.addEventListener("load", startTests, true); +} + +function runTest(browser, url, next) { + gFrontNotificationsPos = 0; + gAllNotificationsPos = 0; + gNextTest = next; + gTestBrowser = browser; + browser.loadURI(url); +} + +function startTests() { + gForegroundBrowser.removeEventListener("load", startTests, true); + executeSoon(startTest1); +} + +function startTest1() { + info("\nTest 1"); + getBrowser().addProgressListener(gFrontProgressListener); + getBrowser().addTabsProgressListener(gAllProgressListener); + + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + "onStateChange" + ]; + gFrontNotifications = gAllNotifications; + runTest(gForegroundBrowser, "http://example.org" + gTestPage, startTest2); +} + +function startTest2() { + info("\nTest 2"); + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + "onSecurityChange", + "onStateChange" + ]; + gFrontNotifications = gAllNotifications; + runTest(gForegroundBrowser, "https://example.com" + gTestPage, startTest3); +} + +function startTest3() { + info("\nTest 3"); + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + "onStateChange" + ]; + gFrontNotifications = []; + runTest(gBackgroundBrowser, "http://example.org" + gTestPage, startTest4); +} + +function startTest4() { + info("\nTest 4"); + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + "onSecurityChange", + "onStateChange" + ]; + gFrontNotifications = []; + runTest(gBackgroundBrowser, "https://example.com" + gTestPage, startTest5); +} + +function startTest5() { + info("\nTest 5"); + // Switch the foreground browser + [gForegroundBrowser, gBackgroundBrowser] = [gBackgroundBrowser, gForegroundBrowser]; + [gForegroundTab, gBackgroundTab] = [gBackgroundTab, gForegroundTab]; + // Avoid the onLocationChange this will fire + getBrowser().removeProgressListener(gFrontProgressListener); + getBrowser().selectedTab = gForegroundTab; + getBrowser().addProgressListener(gFrontProgressListener); + + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + "onStateChange" + ]; + gFrontNotifications = gAllNotifications; + runTest(gForegroundBrowser, "http://example.org" + gTestPage, startTest6); +} + +function startTest6() { + info("\nTest 6"); + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + "onStateChange" + ]; + gFrontNotifications = []; + runTest(gBackgroundBrowser, "http://example.org" + gTestPage, finishTest); +} + +function finishTest() { + getBrowser().removeProgressListener(gFrontProgressListener); + getBrowser().removeTabsProgressListener(gAllProgressListener); + getBrowser().removeTab(gBackgroundTab); + getBrowser().removeTab(gForegroundTab); + finish(); +} diff --git a/comm/suite/browser/test/browser/browser_bug329212.js b/comm/suite/browser/test/browser/browser_bug329212.js new file mode 100644 index 0000000000..86925a4cb8 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug329212.js @@ -0,0 +1,42 @@ +function test() { + waitForExplicitFinish(); + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function loadListener() { + gBrowser.selectedBrowser.removeEventListener("load", loadListener, true); + + let doc = gBrowser.contentDocument; + let tooltip = document.getElementById("aHTMLTooltip"); + + ok(FillInHTMLTooltip(doc.getElementById("svg1")), "should get title"); + is(tooltip.getAttribute("label"), "This is a non-root SVG element title"); + + ok(FillInHTMLTooltip(doc.getElementById("text1")), "should get title"); + is(tooltip.getAttribute("label"), "\n\n\n This is a title\n\n "); + + ok(!FillInHTMLTooltip(doc.getElementById("text2")), "should not get title"); + + ok(!FillInHTMLTooltip(doc.getElementById("text3")), "should not get title"); + + ok(FillInHTMLTooltip(doc.getElementById("link1")), "should get title"); + is(tooltip.getAttribute("label"), "\n This is a title\n "); + ok(FillInHTMLTooltip(doc.getElementById("text4")), "should get title"); + is(tooltip.getAttribute("label"), "\n This is a title\n "); + + ok(!FillInHTMLTooltip(doc.getElementById("link2")), "should not get title"); + + ok(FillInHTMLTooltip(doc.getElementById("link3")), "should get title"); + isnot(tooltip.getAttribute("label"), ""); + + ok(FillInHTMLTooltip(doc.getElementById("link4")), "should get title"); + is(tooltip.getAttribute("label"), "This is an xlink:title attribute"); + + ok(!FillInHTMLTooltip(doc.getElementById("text5")), "should not get title"); + + gBrowser.removeCurrentTab(); + finish(); + }, true); + + content.location = + "http://mochi.test:8888/browser/suite/browser/test/browser/title_test.svg"; +} + diff --git a/comm/suite/browser/test/browser/browser_bug409624.js b/comm/suite/browser/test/browser/browser_bug409624.js new file mode 100644 index 0000000000..c3a800dffa --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug409624.js @@ -0,0 +1,57 @@ +/* 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/. */ +ChromeUtils.defineModuleGetter(this, "FormHistory", + "resource://gre/modules/FormHistory.jsm"); + +function test() { + waitForExplicitFinish(); + + // This test relies on the form history being empty to start with delete + // all the items first. + FormHistory.update({ op: "remove" }, + { handleError: function (error) { + do_throw("Error occurred updating form history: " + error); + }, + handleCompletion: function (reason) { if (!reason) test2(); }, + }); +} + +function test2() +{ + let prefService = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefBranch); + + let findBar = document.getElementById("FindToolbar"); + let textbox = findBar.getElement("findbar-textbox"); + + let temp = {}; + ChromeUtils.import("resource:///modules/Sanitizer.jsm", temp); + let s = temp.Sanitizer; + let prefBranch = prefService.getBranch("privacy.clearOnShutdown."); + + prefBranch.setBoolPref("cache", false); + prefBranch.setBoolPref("cookies", false); + prefBranch.setBoolPref("downloads", false); + prefBranch.setBoolPref("formdata", true); + prefBranch.setBoolPref("history", false); + prefBranch.setBoolPref("offlineApps", false); + prefBranch.setBoolPref("passwords", false); + prefBranch.setBoolPref("sessions", false); + prefBranch.setBoolPref("siteSettings", false); + + prefService.setBoolPref("privacy.sanitize.promptOnSanitize", false); + + // Sanitize now so we can test the baseline point. + s.sanitize(); + ok(!gFindBar.hasTransactions, "pre-test baseline for sanitizer"); + + gFindBar.getElement("findbar-textbox").value = "m"; + ok(gFindBar.hasTransactions, "formdata can be cleared after input"); + + s.sanitize(); + is(gFindBar.getElement("findbar-textbox").value, "", "findBar textbox should be empty after sanitize"); + ok(!gFindBar.hasTransactions, "No transactions after sanitize"); + + finish(); +} diff --git a/comm/suite/browser/test/browser/browser_bug413915.js b/comm/suite/browser/test/browser/browser_bug413915.js new file mode 100644 index 0000000000..98b968a1b3 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug413915.js @@ -0,0 +1,70 @@ +ChromeUtils.defineModuleGetter(this, "Feeds", + "resource:///modules/Feeds.jsm"); + +function test() { + var exampleUri = Services.io.newURI("http://example.com/"); + var secman = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + var principal = secman.createCodebasePrincipal(exampleUri, {}); + + function testIsFeed(aTitle, aHref, aType, aKnown) { + var link = { + title: aTitle, + href: aHref, + type: aType, + ownerDocument: { + characterSet: "UTF-8" + } + }; + return Feeds.isValidFeed(link, principal, aKnown); + } + + var href = "http://example.com/feed/"; + var atomType = "application/atom+xml"; + var funkyAtomType = " aPPLICAtion/Atom+XML "; + var rssType = "application/rss+xml"; + var funkyRssType = " Application/RSS+XML "; + var rdfType = "application/rdf+xml"; + var texmlType = "text/xml"; + var appxmlType = "application/xml"; + var noRss = "Foo"; + var rss = "RSS"; + + // things that should be valid + ok(testIsFeed(noRss, href, atomType, false) == atomType, + "detect Atom feed"); + ok(testIsFeed(noRss, href, funkyAtomType, false) == atomType, + "clean up and detect Atom feed"); + ok(testIsFeed(noRss, href, rssType, false) == rssType, + "detect RSS feed"); + ok(testIsFeed(noRss, href, funkyRssType, false) == rssType, + "clean up and detect RSS feed"); + + // things that should not be feeds + ok(testIsFeed(noRss, href, rdfType, false) == null, + "should not detect RDF non-feed"); + ok(testIsFeed(rss, href, rdfType, false) == null, + "should not detect RDF feed from type and title"); + ok(testIsFeed(noRss, href, texmlType, false) == null, + "should not detect text/xml non-feed"); + ok(testIsFeed(rss, href, texmlType, false) == null, + "should not detect text/xml feed from type and title"); + ok(testIsFeed(noRss, href, appxmlType, false) == null, + "should not detect application/xml non-feed"); + ok(testIsFeed(rss, href, appxmlType, false) == null, + "should not detect application/xml feed from type and title"); + + // security check only, returns cleaned up type or "application/rss+xml" + ok(testIsFeed(noRss, href, atomType, true) == atomType, + "feed security check should return Atom type"); + ok(testIsFeed(noRss, href, funkyAtomType, true) == atomType, + "feed security check should return cleaned up Atom type"); + ok(testIsFeed(noRss, href, rssType, true) == rssType, + "feed security check should return RSS type"); + ok(testIsFeed(noRss, href, funkyRssType, true) == rssType, + "feed security check should return cleaned up RSS type"); + ok(testIsFeed(noRss, href, "", true) == rssType, + "feed security check without type should return RSS type"); + ok(testIsFeed(noRss, href, "garbage", true) == "garbage", + "feed security check with garbage type should return garbage"); +} diff --git a/comm/suite/browser/test/browser/browser_bug427559.js b/comm/suite/browser/test/browser/browser_bug427559.js new file mode 100644 index 0000000000..bd34d89f5c --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug427559.js @@ -0,0 +1,39 @@ +/* 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 bug 427559 to make sure focused elements that are no longer on the page + * will have focus transferred to the window when changing tabs back to that + * tab with the now-gone element. + */ + +// Default focus on a button and have it kill itself on blur +var testPage = 'data:text/html,<body><button onblur="this.remove();"><script>document.body.firstChild.focus();</script></body>'; + +function test() { + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + + gBrowser.selectedBrowser.addEventListener("load", function loadListener() { + gBrowser.selectedBrowser.removeEventListener("load", loadListener, true); + executeSoon(function () { + + // The test page loaded, so open an empty tab, select it, then restore + // the test tab. This causes the test page's focused element to be removed + // from its document. + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.removeCurrentTab(); + + // Make sure focus is given to the window because the element is now gone. + is(document.commandDispatcher.focusedWindow, window.content, + "content window is focused"); + + gBrowser.removeCurrentTab(); + finish(); + }); + }, true); + + content.location = testPage; +} diff --git a/comm/suite/browser/test/browser/browser_bug435325.js b/comm/suite/browser/test/browser/browser_bug435325.js new file mode 100644 index 0000000000..dca45a6001 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug435325.js @@ -0,0 +1,56 @@ +/* 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 makes the browser go online. See bug 435325. */ + +var proxyPrefValue; + +function test() { + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + window.addEventListener("DOMContentLoaded", checkPage); + + + // 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); + + // Go offline and disable the proxy and cache, then try to load the test URL. + Services.io.offline = true; + Services.prefs.setBoolPref("browser.cache.disk.enable", false); + Services.prefs.setBoolPref("browser.cache.memory.enable", false); + content.location = "http://example.com/"; +} + +function checkPage() { + if(content.location == "about:blank") { + info("got about:blank, which is expected once, so return"); + return; + } + + window.removeEventListener("DOMContentLoaded", checkPage); + + ok(Services.io.offline, "Setting Services.io.offline to true."); + is(gBrowser.contentDocument.documentURI.substring(0,27), + "about:neterror?e=netOffline", "Loading the Offline mode neterror page."); + + // Now press the "Try Again" button + ok(gBrowser.contentDocument.getElementById("errorTryAgain"), + "The error page has got a #errorTryAgain element"); + gBrowser.contentDocument.getElementById("errorTryAgain").click(); + + ok(!Services.io.offline, "After clicking the Try Again button, we're back " + + "online."); + + finish(); +} + +registerCleanupFunction(function() { + Services.prefs.setIntPref("network.proxy.type", proxyPrefValue); + Services.prefs.setBoolPref("browser.cache.disk.enable", true); + Services.prefs.setBoolPref("browser.cache.memory.enable", true); + Services.io.offline = false; + gBrowser.removeCurrentTab(); +}); diff --git a/comm/suite/browser/test/browser/browser_bug462289.js b/comm/suite/browser/test/browser/browser_bug462289.js new file mode 100644 index 0000000000..c61b9a0403 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug462289.js @@ -0,0 +1,87 @@ +// Wanted delay (in ms) to let UI fully update. +// 375: hopefully enough (on slow test environments). +var gDelay = 375; + +var tab1, tab2; + +function focus_in_navbar() +{ + var parent = document.activeElement.parentNode; + while (parent && parent.id != "nav-bar") + parent = parent.parentNode; + + return parent != null; +} + +function test() +{ + waitForExplicitFinish(); + + // Ftr, SeaMonkey doesn't support animation (yet). + tab1 = gBrowser.addTab("about:blank"); + tab2 = gBrowser.addTab("about:blank"); + + EventUtils.synthesizeMouseAtCenter(tab1, {}); + setTimeout(step2, gDelay); +} + +function step2() +{ + is(gBrowser.selectedTab, tab1, "1st click on tab1 selects tab"); + isnot(document.activeElement, tab1, "1st click on tab1 does not activate tab"); + + EventUtils.synthesizeMouseAtCenter(tab1, {}); + setTimeout(step3, gDelay); +} + +function step3() +{ + is(gBrowser.selectedTab, tab1, "2nd click on selected tab1 keeps tab selected"); + // SeaMonkey differs from Firefox. + is(document.activeElement, tab1, "2nd click on selected tab1 activates tab"); + + // Ftr, SeaMonkey doesn't support tabsontop (yet). + ok(true, "focusing URLBar then sending Tab(s) until out of nav-bar."); + document.getElementById("urlbar").focus(); + while (focus_in_navbar()) + EventUtils.synthesizeKey("VK_TAB", { }); + is(gBrowser.selectedTab, tab1, "tab key to selected tab1 keeps tab selected"); + is(document.activeElement, tab1, "tab key to selected tab1 activates tab"); + + EventUtils.synthesizeMouseAtCenter(tab1, {}); + setTimeout(step4, gDelay); +} + +function step4() +{ + is(gBrowser.selectedTab, tab1, "3rd click on activated tab1 keeps tab selected"); + is(document.activeElement, tab1, "3rd click on activated tab1 keeps tab activated"); + + EventUtils.synthesizeMouseAtCenter(tab2, {}); + setTimeout(step5, gDelay); +} + +function step5() +{ + // The tabbox selects a tab within a setTimeout in a bubbling mousedown event + // listener, and focuses the current tab if another tab previously had focus. + is(gBrowser.selectedTab, tab2, "click on tab2 while tab1 is activated selects tab"); + is(document.activeElement, tab2, "click on tab2 while tab1 is activated activates tab"); + + ok(true, "focusing content then sending middle-button mousedown to tab2."); + content.focus(); + EventUtils.synthesizeMouseAtCenter(tab2, {button: 1, type: "mousedown"}); + setTimeout(step6, gDelay); +} + +function step6() +{ + is(gBrowser.selectedTab, tab2, "middle-button mousedown on selected tab2 keeps tab selected"); + // SeaMonkey differs from Firefox. + is(document.activeElement, tab2, "middle-button mousedown on selected tab2 activates tab"); + + gBrowser.removeTab(tab2); + gBrowser.removeTab(tab1); + + finish(); +} diff --git a/comm/suite/browser/test/browser/browser_bug519216.js b/comm/suite/browser/test/browser/browser_bug519216.js new file mode 100644 index 0000000000..a924f7a091 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug519216.js @@ -0,0 +1,50 @@ +function test() { + waitForExplicitFinish(); + gBrowser.stop(); + gBrowser.addProgressListener(progressListener1); + gBrowser.addProgressListener(progressListener2); + gBrowser.addProgressListener(progressListener3); + gBrowser.loadURI("data:text/plain,bug519216"); +} + +var calledListener1 = false; +var progressListener1 = { + onLocationChange: function onLocationChange() { + calledListener1 = true; + gBrowser.removeProgressListener(this); + } +}; + +var calledListener2 = false; +var progressListener2 = { + onLocationChange: function onLocationChange() { + ok(calledListener1, "called progressListener1 before progressListener2"); + calledListener2 = true; + gBrowser.removeProgressListener(this); + } +}; + +var progressListener3 = { + onLocationChange: function onLocationChange() { + ok(calledListener2, "called progressListener2 before progressListener3"); + gBrowser.removeProgressListener(this); + gBrowser.addProgressListener(progressListener4); + executeSoon(function () { + expectListener4 = true; + gBrowser.reload(); + }); + } +}; + +var expectListener4 = false; +var progressListener4 = { + onLocationChange: function onLocationChange() { + ok(expectListener4, "didn't call progressListener4 for the first location change"); + gBrowser.removeProgressListener(this); + executeSoon(function () { + gBrowser.addTab(); + gBrowser.removeCurrentTab(); + finish(); + }); + } +}; diff --git a/comm/suite/browser/test/browser/browser_bug561636.js b/comm/suite/browser/test/browser/browser_bug561636.js new file mode 100644 index 0000000000..8f41629253 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug561636.js @@ -0,0 +1,459 @@ +var gInvalidFormPopup = document.getElementById('invalid-form-popup'); +ok(gInvalidFormPopup, + "The browser should have a popup to show when a form is invalid"); + +function checkPopupShow() +{ + ok(gInvalidFormPopup.state == 'showing' || gInvalidFormPopup.state == 'open', + "The invalid form popup should be shown"); +} + +function checkPopupHide() +{ + ok(gInvalidFormPopup.state != 'showing' && gInvalidFormPopup.state != 'open', + "The invalid form popup should not be shown"); +} + +function checkPopupMessage(doc) +{ + is(gInvalidFormPopup.firstChild.textContent, + doc.getElementById('i').validationMessage, + "The panel should show the message from validationMessage"); +} + +var gObserver = { + QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]), + + notifyInvalidSubmit : function (aFormElement, aInvalidElements) + { + } +}; + +function test() +{ + waitForExplicitFinish(); + + test1(); +} + +/** + * In this test, we check that no popup appears if the form is valid. + */ +function test1() { + let uri = "data:text/html,<html><body><iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='s' type='submit'></form></body></html>"; + let tab = gBrowser.addTab(); + + tab.linkedBrowser.addEventListener("load", function test1TabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test1TabLBLoad, true); + let doc = gBrowser.contentDocument; + + doc.getElementById('s').click(); + + executeSoon(function() { + checkPopupHide(); + + // Clean-up + gBrowser.removeTab(gBrowser.selectedTab); + + // Next test + executeSoon(test2); + }); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that, when an invalid form is submitted, + * the invalid element is focused and a popup appears. + */ +function test2() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function test2gIpopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", test2gIpopupShown); + + let doc = gBrowser.contentDocument; + is(doc.activeElement, doc.getElementById('i'), + "First invalid element should be focused"); + + checkPopupShow(); + checkPopupMessage(doc); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab); + executeSoon(test3); + }); + + tab.linkedBrowser.addEventListener("load", function test2TabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test2TabLBLoad, true); + + gBrowser.contentDocument.getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that, when an invalid form is submitted, + * the first invalid element is focused and a popup appears. + */ +function test3() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='i' required><input required><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function test3gIPopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", test3gIPopupShown); + + let doc = gBrowser.contentDocument; + is(doc.activeElement, doc.getElementById('i'), + "First invalid element should be focused"); + + checkPopupShow(); + checkPopupMessage(doc); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab); + executeSoon(test4a); + }); + + tab.linkedBrowser.addEventListener("load", function test3TabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test3TabLBLoad, true); + + gBrowser.contentDocument.getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that, we hide the popup by interacting with the + * invalid element if the element becomes valid. + */ +function test4a() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function test4agIPopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", test4agIPopupShown); + + let doc = gBrowser.contentDocument; + is(doc.activeElement, doc.getElementById('i'), + "First invalid element should be focused"); + + checkPopupShow(); + checkPopupMessage(doc); + + EventUtils.synthesizeKey("a", {}); + + executeSoon(function () { + checkPopupHide(); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab); + executeSoon(test4b); + }); + }); + + tab.linkedBrowser.addEventListener("load", function test4aTabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test4aTabLBLoad, true); + + gBrowser.contentDocument.getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that, we don't hide the popup by interacting with the + * invalid element if the element is still invalid. + */ +function test4b() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' id='i' required><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function test4bgIpopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", test4bgIpopupShown); + + let doc = gBrowser.contentDocument; + is(doc.activeElement, doc.getElementById('i'), + "First invalid element should be focused"); + + checkPopupShow(); + checkPopupMessage(doc); + + EventUtils.synthesizeKey("a", {}); + + executeSoon(function () { + checkPopupShow(); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab); + executeSoon(test5); + }); + }); + + tab.linkedBrowser.addEventListener("load", function test4bTabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test4bTabLBLoad, true); + + gBrowser.contentDocument.getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that we can hide the popup by blurring the invalid + * element. + */ +function test5() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function test5gIpopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", test5gIpopupShown); + + let doc = gBrowser.contentDocument; + is(doc.activeElement, doc.getElementById('i'), + "First invalid element should be focused"); + + checkPopupShow(); + checkPopupMessage(doc); + + doc.getElementById('i').blur(); + + executeSoon(function () { + checkPopupHide(); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab); + executeSoon(test6); + }); + }); + + tab.linkedBrowser.addEventListener("load", function test5TabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test5TabLBLoad, true); + + gBrowser.contentDocument.getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that we can hide the popup by pressing TAB. + */ +function test6() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function test6gIpopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", test6gIpopupShown); + + let doc = gBrowser.contentDocument; + is(doc.activeElement, doc.getElementById('i'), + "First invalid element should be focused"); + + checkPopupShow(); + checkPopupMessage(doc); + + EventUtils.synthesizeKey("VK_TAB", {}); + + executeSoon(function () { + checkPopupHide(); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab); + executeSoon(test7); + }); + }); + + tab.linkedBrowser.addEventListener("load", function test6TabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test6TabLBLoad, true); + + gBrowser.contentDocument.getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that the popup will hide if we move to another tab. + */ +function test7() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function test7gIpopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", test7gIpopupShown); + + let doc = gBrowser.contentDocument; + is(doc.activeElement, doc.getElementById('i'), + "First invalid element should be focused"); + + checkPopupShow(); + checkPopupMessage(doc); + + // Create a new tab and move to it. + // Ftr, SeaMonkey doesn't support animation (yet). + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + + executeSoon(function() { + checkPopupHide(); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab); + gBrowser.removeTab(gBrowser.selectedTab); + executeSoon(test8); + }); + }); + + tab.linkedBrowser.addEventListener("load", function test7TabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test7TabLBLoad, true); + + gBrowser.contentDocument.getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that nothing happen (no focus nor popup) if the + * invalid form is submitted in another tab than the current focused one + * (submitted in background). + */ +function test8() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gObserver.notifyInvalidSubmit = function() { + executeSoon(function() { + let doc = tab.linkedBrowser.contentDocument; + isnot(doc.activeElement, doc.getElementById('i'), + "We should not focus the invalid element when the form is submitted in background"); + + checkPopupHide(); + + // Clean-up + Services.obs.removeObserver(gObserver, "invalidformsubmit"); + gObserver.notifyInvalidSubmit = function () {}; + gBrowser.removeTab(tab); + + // Next test + executeSoon(test9); + }); + }; + + Services.obs.addObserver(gObserver, "invalidformsubmit"); + + tab.linkedBrowser.addEventListener("load", function test8TabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test8TabLBLoad, true); + + isnot(gBrowser.selectedTab, tab, + "This tab should have been loaded in background"); + + tab.linkedBrowser.contentDocument.getElementById('s').click(); + }, true); + + tab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that the author defined error message is shown. + */ +function test9() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input x-moz-errormessage='foo' required id='i'><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function test9gIpopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", test9gIpopupShown); + + let doc = gBrowser.contentDocument; + is(doc.activeElement, doc.getElementById('i'), + "First invalid element should be focused"); + + checkPopupShow(); + + is(gInvalidFormPopup.firstChild.textContent, "foo", + "The panel should show the author defined error message"); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab); + executeSoon(test10); + }); + + tab.linkedBrowser.addEventListener("load", function test9TabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test9TabLBLoad, true); + + gBrowser.contentDocument.getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} + +/** + * In this test, we check that the message is correctly updated when it changes. + */ +function test10() +{ + let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' required id='i'><input id='s' type='submit'></form>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function test10gIpopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", test10gIpopupShown); + + let doc = gBrowser.contentDocument; + let input = doc.getElementById('i'); + is(doc.activeElement, input, "First invalid element should be focused"); + + checkPopupShow(); + + is(gInvalidFormPopup.firstChild.textContent, input.validationMessage, + "The panel should show the current validation message"); + + input.addEventListener('input', function test10InputInput() { + input.removeEventListener('input', test10InputInput); + + executeSoon(function() { + // Now, the element suffers from another error, the message should have + // been updated. + is(gInvalidFormPopup.firstChild.textContent, input.validationMessage, + "The panel should show the current validation message"); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab); + executeSoon(finish); + }); + }); + + EventUtils.synthesizeKey('f', {}); + }); + + tab.linkedBrowser.addEventListener("load", function test10TabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", test10TabLBLoad, true); + + gBrowser.contentDocument.getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} diff --git a/comm/suite/browser/test/browser/browser_bug562649.js b/comm/suite/browser/test/browser/browser_bug562649.js new file mode 100644 index 0000000000..57524a4b82 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug562649.js @@ -0,0 +1,26 @@ +function test() { + const URI = "data:text/plain,bug562649"; + browserDOMWindow.openURI(makeURI(URI), + null, + Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, + Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL); + + is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI"); + is(gURLBar.value, URI, "location bar value matches test URI"); + + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.removeCurrentTab(); + is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI after switching tabs"); + is(gURLBar.value, URI, "location bar value matches test URI after switching tabs"); + + waitForExplicitFinish(); + gBrowser.selectedBrowser.addEventListener("load", function loadListener() { + gBrowser.selectedBrowser.removeEventListener("load", loadListener, true); + + is(gBrowser.userTypedValue, null, "userTypedValue is null as the page has loaded"); + is(gURLBar.value, URI, "location bar value matches test URI as the page has loaded"); + + gBrowser.removeCurrentTab(); + finish(); + }, true); +} diff --git a/comm/suite/browser/test/browser/browser_bug581947.js b/comm/suite/browser/test/browser/browser_bug581947.js new file mode 100644 index 0000000000..5ac81b1218 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug581947.js @@ -0,0 +1,95 @@ +function check(aElementName, aBarred, aType) { + let doc = gBrowser.contentDocument; + let tooltip = document.getElementById("aHTMLTooltip"); + let content = doc.getElementById('content'); + + let e = doc.createElement(aElementName); + content.appendChild(e); + + if (aType) { + e.type = aType; + } + + ok(!FillInHTMLTooltip(e), + "No tooltip should be shown when the element is valid"); + + e.setCustomValidity('foo'); + if (aBarred) { + ok(!FillInHTMLTooltip(e), + "No tooltip should be shown when the element is barred from constraint validation"); + } else { + ok(FillInHTMLTooltip(e), + e.tagName + " " +"A tooltip should be shown when the element isn't valid"); + } + + e.setAttribute('title', ''); + ok (!FillInHTMLTooltip(e), + "No tooltip should be shown if the title attribute is set"); + + e.removeAttribute('title'); + content.setAttribute('novalidate', ''); + ok (!FillInHTMLTooltip(e), + "No tooltip should be shown if the novalidate attribute is set on the form owner"); + content.removeAttribute('novalidate'); + + e.remove(); +} + +function todo_check(aElementName, aBarred) { + let doc = gBrowser.contentDocument; + let tooltip = document.getElementById("aHTMLTooltip"); + let content = doc.getElementById('content'); + + let e = doc.createElement(aElementName); + content.appendChild(e); + + let caught = false; + try { + e.setCustomValidity('foo'); + } catch (e) { + caught = true; + } + + todo(!caught, "setCustomValidity should exist for " + aElementName); + + e.remove(); +} + +function test() { + waitForExplicitFinish(); + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function loadListener() { + gBrowser.selectedBrowser.removeEventListener("load", loadListener, true); + + let testData = [ + /* element name, barred */ + [ 'input', false, null], + [ 'textarea', false, null], + [ 'button', true, 'button'], + [ 'button', false, 'submit'], + [ 'select', false, null], + [ 'output', true, null], + [ 'fieldset', true, null], + [ 'object', true, null] + ]; + + for (let data of testData) { + check(data[0], data[1], data[2]); + } + + let todo_testData = [ + [ 'keygen', 'false' ] + ]; + + for (let data of todo_testData) { + todo_check(data[0], data[1]); + } + + gBrowser.removeCurrentTab(); + finish(); + }, true); + + content.location = + "data:text/html,<!DOCTYPE html><html><body><form id='content'></form></body></html>"; +} + diff --git a/comm/suite/browser/test/browser/browser_bug585511.js b/comm/suite/browser/test/browser/browser_bug585511.js new file mode 100644 index 0000000000..f6513cac5a --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug585511.js @@ -0,0 +1,24 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function test() { + is(getBrowser().tabs.length, 1, "one tab is open initially"); + is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync"); + + // Add several new tabs + let tab1 = getBrowser().addTab("http://mochi.test:8888/#1"); + let tab2 = getBrowser().addTab("http://mochi.test:8888/#2"); + let tab3 = getBrowser().addTab("http://mochi.test:8888/#3"); + is(getBrowser().tabs.length, 4, "four tabs are open"); + is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync"); + getBrowser().removeTab(tab2); + is(getBrowser().tabs.length, 3, "three tabs are open"); + is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync"); + getBrowser().removeTab(tab1); + is(getBrowser().tabs.length, 2, "two tabs are open"); + is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync"); + getBrowser().removeTab(tab3); + is(getBrowser().tabs.length, 1, "we've closed all our tabs"); + is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync"); +} diff --git a/comm/suite/browser/test/browser/browser_bug595507.js b/comm/suite/browser/test/browser/browser_bug595507.js new file mode 100644 index 0000000000..0bea8cabc6 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug595507.js @@ -0,0 +1,39 @@ +var gInvalidFormPopup = document.getElementById('invalid-form-popup'); +ok(gInvalidFormPopup, + "The browser should have a popup to show when a form is invalid"); + +/** + * Make sure that the form validation error message shows even if the form is in an iframe. + */ +function test() +{ + waitForExplicitFinish(); + + let uri = "data:text/html,<iframe src=\"data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>\"</iframe>"; + let tab = gBrowser.addTab(); + + gInvalidFormPopup.addEventListener("popupshown", function testgIpopupShown() { + gInvalidFormPopup.removeEventListener("popupshown", testgIpopupShown); + + let doc = gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument; + is(doc.activeElement, doc.getElementById('i'), + "First invalid element should be focused"); + + ok(gInvalidFormPopup.state == 'showing' || gInvalidFormPopup.state == 'open', + "The invalid form popup should be shown"); + + // Clean-up and next test. + gBrowser.removeTab(gBrowser.selectedTab, {animate: false}); + executeSoon(finish); + }); + + tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) { + tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true); + + gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument + .getElementById('s').click(); + }, true); + + gBrowser.selectedTab = tab; + gBrowser.selectedTab.linkedBrowser.loadURI(uri); +} diff --git a/comm/suite/browser/test/browser/browser_bug623155.js b/comm/suite/browser/test/browser/browser_bug623155.js new file mode 100644 index 0000000000..df8197a565 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_bug623155.js @@ -0,0 +1,136 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const REDIRECT_FROM = "https://example.com/browser/suite/browser/test/browser/" + + "redirect_bug623155.sjs"; + +const REDIRECT_TO = "https://www.bank1.com/"; // Bad-cert host. + +function isRedirectedURISpec(aURISpec) { + return isRedirectedURI(Services.io.newURI(aURISpec)); +} + +function isRedirectedURI(aURI) { + // Compare only their before-hash portion. + return Services.io.newURI(REDIRECT_TO) + .equalsExceptRef(aURI); +} + +/* + Test. + +1. Load +https://example.com/browser/browser/base/content/test/redirect_bug623155.sjs#BG + in a background tab. + +2. The redirected URI is <https://www.bank1.com/#BG>, which displayes a cert + error page. + +3. Switch the tab to foreground. + +4. Check the URLbar's value, expecting <https://www.bank1.com/#BG> + +5. Load +https://example.com/browser/browser/base/content/test/redirect_bug623155.sjs#FG + in the foreground tab. + +6. The redirected URI is <https://www.bank1.com/#FG>. And this is also + a cert-error page. + +7. Check the URLbar's value, expecting <https://www.bank1.com/#FG> + +8. End. + + */ + +var gNewTab; + +function test() { + waitForExplicitFinish(); + + // Load a URI in the background. + gNewTab = gBrowser.addTab(REDIRECT_FROM + "#BG"); + gBrowser.getBrowserForTab(gNewTab) + .webProgress + .addProgressListener(gWebProgressListener, + Ci.nsIWebProgress + .NOTIFY_LOCATION); +} + +var gWebProgressListener = { + QueryInterface: function(aIID) { + if (aIID.equals(Ci.nsIWebProgressListener) || + aIID.equals(Ci.nsISupportsWeakReference) || + aIID.equals(Ci.nsISupports)) + return this; + throw Cr.NS_NOINTERFACE; + }, + + // --------------------------------------------------------------------------- + // NOTIFY_LOCATION mode should work fine without these methods. + // + //onStateChange: function() {}, + //onStatusChange: function() {}, + //onProgressChange: function() {}, + //onSecurityChange: function() {}, + //---------------------------------------------------------------------------- + + onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) { + if (!aRequest) { + // This is bug 673752, or maybe initial "about:blank". + return; + } + + ok(gNewTab, "There is a new tab."); + ok(isRedirectedURI(aLocation), + "onLocationChange catches only redirected URI."); + + if (aLocation.ref == "BG") { + // This is background tab's request. + isnot(gNewTab, gBrowser.selectedTab, "This is a background tab."); + } else if (aLocation.ref == "FG") { + // This is foreground tab's request. + is(gNewTab, gBrowser.selectedTab, "This is a foreground tab."); + } + else { + // We shonuld not reach here. + ok(false, "This URI hash is not expected:" + aLocation.ref); + } + + let isSelectedTab = (gNewTab == gBrowser.selectedTab); + setTimeout(delayed, 0, isSelectedTab); + } +}; + +function delayed(aIsSelectedTab) { + // Switch tab and confirm URL bar. + if (!aIsSelectedTab) { + gBrowser.selectedTab = gNewTab; + } + + ok(isRedirectedURISpec(content.location.href), + "The content area is redirected. aIsSelectedTab:" + aIsSelectedTab); + is(gURLBar.value, content.location.href, + "The URL bar shows the content URI. aIsSelectedTab:" + aIsSelectedTab); + + if (!aIsSelectedTab) { + // If this was a background request, go on a foreground request. + content.location = REDIRECT_FROM + "#FG"; + } + else { + // Othrewise, nothing to do remains. + finish(); + } +} + +/* Cleanup */ +registerCleanupFunction(function() { + if (gNewTab) { + gBrowser.getBrowserForTab(gNewTab) + .webProgress + .removeProgressListener(gWebProgressListener); + + gBrowser.removeTab(gNewTab); + } + gNewTab = null; +}); diff --git a/comm/suite/browser/test/browser/browser_ctrlTab.js b/comm/suite/browser/test/browser/browser_ctrlTab.js new file mode 100644 index 0000000000..aea07917a3 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_ctrlTab.js @@ -0,0 +1,197 @@ +function test() { + waitForExplicitFinish(); + + gBrowser.addTab(); + gBrowser.addTab(); + gBrowser.addTab(); + + assertTabs(4); + + ctrlTabTest([2] , 1, 0); + ctrlTabTest([2, 3, 1], 2, 2); + ctrlTabTest([] , 4, 2); + + { + let selectedIndex = gBrowser.tabContainer.selectedIndex; + pressCtrlTab(); + pressCtrlTab(true); + releaseCtrl(); + is(gBrowser.tabContainer.selectedIndex, selectedIndex, + "Ctrl+Tab -> Ctrl+Shift+Tab keeps the selected tab"); + } + + { // test for bug 445369 + let tabs = gBrowser.tabs.length; + pressCtrlTab(); + EventUtils.synthesizeKey("w", { ctrlKey: true }); + is(gBrowser.tabs.length, tabs - 1, "Ctrl+Tab -> Ctrl+W removes one tab"); + releaseCtrl(); + } + assertTabs(3); + + ctrlTabTest([2, 1, 0], 7, 1); + + { // test for bug 445369 + selectTabs([1, 2, 0]); + + let selectedTab = gBrowser.selectedTab; + let tabToRemove = gBrowser.tabs[1]; + + pressCtrlTab(); + pressCtrlTab(); + EventUtils.synthesizeKey("w", { ctrlKey: true }); + ok(!tabToRemove.parentNode, + "Ctrl+Tab*2 -> Ctrl+W removes the second most recently selected tab"); + + pressCtrlTab(true); + pressCtrlTab(true); + releaseCtrl(); + ok(gBrowser.selectedTab == selectedTab, + "Ctrl+Tab*2 -> Ctrl+W -> Ctrl+Shift+Tab*2 keeps the selected tab"); + } + assertTabs(2); + + ctrlTabTest([1], 1, 0); + + gBrowser.removeTab(gBrowser.tabContainer.lastChild); + + assertTabs(1); + + { // test for bug 445768 + let focusedWindow = document.commandDispatcher.focusedWindow; + let eventConsumed = true; + let detectKeyEvent = function (event) { + eventConsumed = event.defaultPrevented; + }; + document.addEventListener("keypress", detectKeyEvent); + pressCtrlTab(); + document.removeEventListener("keypress", detectKeyEvent); + ok(eventConsumed, "Ctrl+Tab consumed by the tabbed browser if one tab is open"); + is(focusedWindow.location, document.commandDispatcher.focusedWindow.location, + "Ctrl+Tab doesn't change focus if one tab is open"); + } + + gBrowser.addTab(); + gBrowser.addTab(); + gBrowser.addTab(); + + assertTabs(4); + selectTabs([0, 1, 2, 3]); + pressCtrlTab(); + ctrlTab.panel.addEventListener("popupshown", stickyTests); + + function stickyTests() { + ctrlTab.panel.removeEventListener("popupshown", stickyTests); + + EventUtils.synthesizeKey("f", { ctrlKey: true }); + is(document.activeElement, ctrlTab.searchField.inputField, + "Ctrl+Tab -> Ctrl+F focuses the panel's search field"); + + releaseCtrl(); + ok(isOpen(), + "panel is sticky after focusing the search field and releasing the Ctrl key"); + + EventUtils.synthesizeKey("f", {}); + EventUtils.synthesizeKey("o", {}); + EventUtils.synthesizeKey("o", {}); + is(ctrlTab.searchField.value, "foo", + "text entered into search field"); + + EventUtils.synthesizeKey("VK_RETURN", {}); + ok(isOpen(), + "Enter key kicks pending search off; the panel stays open as there's no match"); + is(ctrlTab.searchField.value, "foo", + "search field value persists after Enter pressed"); + + EventUtils.synthesizeKey("VK_ESCAPE", {}); + is(ctrlTab.searchField.value, "", + "ESC key clears the search field"); + ok(isOpen(), + "Clearing the search field with ESC keeps the panel open"); + + // blur the search field + EventUtils.synthesizeKey("VK_TAB", {}); + isnot(document.activeElement, ctrlTab.searchField.inputField, + "Tab key blurs the panel's search field"); + + // advance selection and close panel + EventUtils.synthesizeKey("VK_TAB", {}); + EventUtils.synthesizeKey("VK_TAB", {}); + EventUtils.synthesizeKey("VK_RETURN", {}); + ok(!isOpen(), + "Enter key closes the panel"); + is(gBrowser.tabContainer.selectedIndex, 1, + "Tab key advances the selection while the panel is sticky"); + + gBrowser.removeCurrentTab(); + gBrowser.removeCurrentTab(); + gBrowser.removeCurrentTab(); + assertTabs(1); + finish(); + } + + + /* private utility functions */ + + function pressCtrlTab(aShiftKey) { + EventUtils.synthesizeKey("VK_TAB", { ctrlKey: true, shiftKey: !!aShiftKey }); + } + + function releaseCtrl() { + EventUtils.synthesizeKey("VK_CONTROL", { type: "keyup" }); + } + + function isOpen() { + return ctrlTab.panel.state == "showing" || ctrlTab.panel.state == "open"; + } + + function assertTabs(aTabs) { + var tabs = gBrowser.tabs.length; + if (tabs != aTabs) { + while (gBrowser.tabs.length > 1) + gBrowser.removeCurrentTab(); + throw "expected " + aTabs + " open tabs, got " + tabs; + } + } + + function selectTabs(tabs) { + tabs.forEach(function (index) { + gBrowser.selectedTab = gBrowser.tabs[index]; + }); + } + + function ctrlTabTest(tabsToSelect, tabTimes, expectedIndex) { + selectTabs(tabsToSelect); + + var indexStart = gBrowser.tabContainer.selectedIndex; + var tabCount = gBrowser.tabs.length; + var normalized = tabTimes % tabCount; + var where = normalized == 1 ? "back to the previously selected tab" : + normalized + " tabs back in most-recently-selected order"; + + for (let i = 0; i < tabTimes; i++) { + pressCtrlTab(); + + if (tabCount > 2) + is(gBrowser.tabContainer.selectedIndex, indexStart, + "Selected tab doesn't change while tabbing"); + } + + if (tabCount > 2) { + ok(isOpen(), + "With " + tabCount + " tabs open, Ctrl+Tab opens the preview panel"); + + releaseCtrl(); + + ok(!isOpen(), + "Releasing Ctrl closes the preview panel"); + } else { + ok(!isOpen(), + "With " + tabCount + " tabs open, Ctrl+Tab doesn't open the preview panel"); + } + + is(gBrowser.tabContainer.selectedIndex, expectedIndex, + "With "+ tabCount +" tabs open and tab " + indexStart + + " selected, Ctrl+Tab*" + tabTimes + " goes " + where); + } +} diff --git a/comm/suite/browser/test/browser/browser_fayt.js b/comm/suite/browser/test/browser/browser_fayt.js new file mode 100644 index 0000000000..8522bc366b --- /dev/null +++ b/comm/suite/browser/test/browser/browser_fayt.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function test() { + var tab1 = gBrowser.addTab("data:text/html;charset=utf-8,<p>this is some dummy text</p>"); + var tab2 = gBrowser.addTab("data:text/html;charset=utf-8,<p>this is some random text</p>"); + + gBrowser.getBrowserForTab(tab2).addEventListener("load", runTest, true); + waitForExplicitFinish(); + + function runTest() { + gBrowser.getBrowserForTab(tab2).removeEventListener("load", runTest, true); + + gBrowser.selectedTab = tab2; + is(gBrowser.fastFind.find("random", false), Ci.nsITypeAheadFind.FIND_FOUND, "FAYT found the random text"); + gBrowser.selectedTab = tab1; + is(gBrowser.fastFind.find("dummy", false), Ci.nsITypeAheadFind.FIND_FOUND, "FAYT found the dummy text"); + + gBrowser.removeTab(tab2); + gBrowser.removeTab(tab1); + finish(); + } +} + diff --git a/comm/suite/browser/test/browser/browser_notification_tab_switching.js b/comm/suite/browser/test/browser/browser_notification_tab_switching.js new file mode 100644 index 0000000000..2cbdaa3147 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_notification_tab_switching.js @@ -0,0 +1,95 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +var tab; +var notification; +var notificationURL = "http://example.org/browser/suite/browser/test/browser/file_dom_notifications.html"; +var newWindowOpenedFromTab; + +function test () { + waitForExplicitFinish(); + + let pm = Services.perms; + registerCleanupFunction(function() { + pm.remove(makeURI(notificationURL), "desktop-notification"); + gBrowser.removeTab(tab); + window.restore(); + }); + + pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION); + + tab = gBrowser.addTab(notificationURL); + tab.linkedBrowser.addEventListener("load", onLoad, true); +} + +function onLoad() { + isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab"); + tab.linkedBrowser.removeEventListener("load", onLoad, true); + let win = tab.linkedBrowser.contentWindow.wrappedJSObject; + win.newWindow = win.open("about:blank", "", "height=100,width=100"); + newWindowOpenedFromTab = win.newWindow; + win.newWindow.addEventListener("load", function() { + info("new window loaded"); + win.newWindow.addEventListener("blur", function b() { + info("new window got blur"); + win.newWindow.removeEventListener("blur", b); + notification = win.showNotification1(); + win.newWindow.addEventListener("focus", onNewWindowFocused); + notification.addEventListener("show", onAlertShowing); + }); + + function waitUntilNewWindowHasFocus() { + if (!win.newWindow.document.hasFocus()) { + setTimeout(waitUntilNewWindowHasFocus, 50); + } else { + // Focus another window so that new window gets blur event. + gBrowser.selectedBrowser.contentWindow.focus(); + } + } + win.newWindow.focus(); + waitUntilNewWindowHasFocus(); + }); +} + +function onAlertShowing() { + info("Notification alert showing"); + notification.removeEventListener("show", onAlertShowing); + + let alertWindow = findChromeWindowByURI("chrome://global/content/alerts/alert.xul"); + if (!alertWindow) { + todo(false, "Notifications don't use XUL windows on all platforms."); + notification.close(); + newWindowOpenedFromTab.close(); + finish(); + return; + } + gBrowser.tabContainer.addEventListener("TabSelect", onTabSelect); + EventUtils.synthesizeMouseAtCenter(alertWindow.document.getElementById("alertTitleLabel"), {}, alertWindow); + info("Clicked on notification"); + alertWindow.close(); +} + +function onNewWindowFocused(event) { + event.target.close(); + isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab"); + // Using timeout to test that something do *not* happen! + setTimeout(openSecondNotification, 50); +} + +function openSecondNotification() { + isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab"); + let win = tab.linkedBrowser.contentWindow.wrappedJSObject; + notification = win.showNotification2(); + notification.addEventListener("show", onAlertShowing); +} + +function onTabSelect() { + gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect); + is(gBrowser.selectedBrowser.contentWindow.location.href, notificationURL, + "Notification tab should be selected."); + + finish(); +} diff --git a/comm/suite/browser/test/browser/browser_pageInfo.js b/comm/suite/browser/test/browser/browser_pageInfo.js new file mode 100644 index 0000000000..399301a784 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_pageInfo.js @@ -0,0 +1,38 @@ +function test() { + waitForExplicitFinish(); + + var pageInfo; + + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function loadListener() { + gBrowser.selectedBrowser.removeEventListener("load", loadListener, true); + + Services.obs.addObserver(observer, "page-info-dialog-loaded"); + pageInfo = BrowserPageInfo(); + }, true); + content.location = + "https://example.com/browser/suite/browser/test/browser/feed_tab.html"; + + function observer(win, topic, data) { + Services.obs.removeObserver(observer, "page-info-dialog-loaded"); + handlePageInfo(); + } + + function handlePageInfo() { + ok(pageInfo.document.getElementById("feedPanel"), "Feed panel"); + let feedListbox = pageInfo.document.getElementById("feedListbox"); + ok(feedListbox, "Feed list"); + + var feedRowsNum = feedListbox.getRowCount(); + is(feedRowsNum, 3, "Number of feeds listed"); + + for (var i = 0; i < feedRowsNum; i++) { + let feedItem = feedListbox.getItemAtIndex(i); + is(feedItem.getAttribute("name"), i + 1, "Feed name"); + } + + pageInfo.close(); + gBrowser.removeCurrentTab(); + finish(); + } +} diff --git a/comm/suite/browser/test/browser/browser_page_style_menu.js b/comm/suite/browser/test/browser/browser_page_style_menu.js new file mode 100644 index 0000000000..88ce97da0b --- /dev/null +++ b/comm/suite/browser/test/browser/browser_page_style_menu.js @@ -0,0 +1,67 @@ +function test() { + waitForExplicitFinish(); + + var tab = gBrowser.addTab(); + gBrowser.selectedTab = tab; + tab.linkedBrowser.addEventListener("load", function loadListener() { + tab.linkedBrowser.removeEventListener("load", loadListener, true); + checkPageStyleMenu(); + }, true); + let rootDir = getRootDirectory(gTestPath); + content.location = rootDir + "page_style_sample.html"; +} + +function checkPageStyleMenu() { + var menupopup = document.getElementById("menu_UseStyleSheet") + .getElementsByTagName("menupopup")[0]; + stylesheetFillPopup(menupopup); + + var items = []; + var current = menupopup.getElementsByTagName("menuitem")[1]; + while (current.nextSibling) { + current = current.nextSibling; + items.push(current); + } + + var validLinks = 0; + Array.from(content.document.getElementsByTagName("link")).forEach(function (link) { + var title = link.getAttribute("title"); + var rel = link.getAttribute("rel"); + var media = link.getAttribute("media"); + var idstring = "link " + (title ? title : "without title and") + + " with rel=\"" + rel + "\"" + + (media ? " and media=\"" + media + "\"" : ""); + + var item = items.filter(item => item.getAttribute("label") == title); + var found = item.length == 1; + var checked = found && (item[0].getAttribute("checked") == "true"); + + switch (link.getAttribute("data-state")) { + case "0": + ok(!found, idstring + " does not show up in page style menu"); + break; + case "0-todo": + validLinks++; + todo(!found, idstring + " should not show up in page style menu"); + ok(!checked, idstring + " is not selected"); + break; + case "1": + validLinks++; + ok(found, idstring + " shows up in page style menu"); + ok(!checked, idstring + " is not selected"); + break; + case "2": + validLinks++; + ok(found, idstring + " shows up in page style menu"); + ok(checked, idstring + " is selected"); + break; + default: + throw "data-state attribute is missing or has invalid value"; + } + }); + + is(validLinks, items.length, "all valid links found"); + + gBrowser.removeCurrentTab(); + finish(); +} diff --git a/comm/suite/browser/test/browser/browser_popupNotification.js b/comm/suite/browser/test/browser/browser_popupNotification.js new file mode 100644 index 0000000000..da88243e66 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_popupNotification.js @@ -0,0 +1,782 @@ +/* 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(); + + ok(PopupNotifications, "PopupNotifications object exists"); + ok(PopupNotifications.panel, "PopupNotifications panel exists"); + + registerCleanupFunction(cleanUp); + + runNextTest(); +} + +function cleanUp() { + for (var topic in gActiveObservers) + Services.obs.removeObserver(gActiveObservers[topic], topic); + for (var eventName in gActiveListeners) + PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName]); +} + +var gActiveListeners = {}; +var gActiveObservers = {}; +var gShownState = {}; + +function runNextTest() { + let nextTest = tests[gTestIndex]; + + function goNext() { + if (++gTestIndex == tests.length) + executeSoon(finish); + else + executeSoon(runNextTest); + } + + function addObserver(topic) { + function observer() { + Services.obs.removeObserver(observer, "PopupNotifications-" + topic); + delete gActiveObservers["PopupNotifications-" + topic]; + + info("[Test #" + gTestIndex + "] observer for " + topic + " called"); + nextTest[topic](); + goNext(); + } + Services.obs.addObserver(observer, "PopupNotifications-" + topic); + gActiveObservers["PopupNotifications-" + topic] = observer; + } + + if (nextTest.backgroundShow) { + addObserver("backgroundShow"); + } else if (nextTest.updateNotShowing) { + addObserver("updateNotShowing"); + } else { + doOnPopupEvent("popupshowing", function () { + info("[Test #" + gTestIndex + "] popup showing"); + }); + doOnPopupEvent("popupshown", function () { + gShownState[gTestIndex] = true; + info("[Test #" + gTestIndex + "] popup shown"); + nextTest.onShown(this); + }); + + // We allow multiple onHidden functions to be defined in an array. They're + // called in the order they appear. + let onHiddenArray = nextTest.onHidden instanceof Array ? + nextTest.onHidden : + [nextTest.onHidden]; + doOnPopupEvent("popuphidden", function () { + if (!gShownState[gTestIndex]) { + // This is expected to happen for test 9, so let's not treat it as a failure. + info("Popup from test " + gTestIndex + " was hidden before its popupshown fired"); + } + + let onHidden = onHiddenArray.shift(); + info("[Test #" + gTestIndex + "] popup hidden (" + onHiddenArray.length + " hides remaining)"); + executeSoon(function () { + onHidden.call(nextTest, this); + if (!onHiddenArray.length) + goNext(); + }); + }, onHiddenArray.length); + info("[Test #" + gTestIndex + "] added listeners; panel state: " + PopupNotifications.isPanelOpen); + } + + info("[Test #" + gTestIndex + "] running test"); + nextTest.run(); +} + +function doOnPopupEvent(eventName, callback, numExpected) { + gActiveListeners[eventName] = function (event) { + if (event.target != PopupNotifications.panel) + return; + if (typeof(numExpected) === "number") + numExpected--; + if (!numExpected) { + PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName]); + delete gActiveListeners[eventName]; + } + + callback.call(PopupNotifications.panel); + } + PopupNotifications.panel.addEventListener(eventName, gActiveListeners[eventName]); +} + +var gTestIndex = 0; +var gNewTab; + +function basicNotification() { + var self = this; + this.browser = gBrowser.selectedBrowser; + this.id = "test-notification-" + gTestIndex; + this.message = "This is popup notification " + this.id + " from test " + gTestIndex; + this.anchorID = null; + this.mainAction = { + label: "Main Action", + accessKey: "M", + callback: function () { + self.mainActionClicked = true; + } + }; + this.secondaryActions = [ + { + label: "Secondary Action", + accessKey: "S", + callback: function () { + self.secondaryActionClicked = true; + } + } + ]; + this.options = { + eventCallback: function (eventName) { + switch (eventName) { + case "dismissed": + self.dismissalCallbackTriggered = true; + break; + case "shown": + self.shownCallbackTriggered = true; + break; + case "removed": + self.removedCallbackTriggered = true; + break; + } + } + }; + this.addOptions = function(options) { + for (let [name, value] of Object.entries(options)) + self.options[name] = value; + } +} + +var wrongBrowserNotificationObject = new basicNotification(); +var wrongBrowserNotification; + +var tests = [ + { // Test #0 + run: function () { + this.notifyObj = new basicNotification(); + showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + triggerMainCommand(popup); + }, + onHidden: function (popup) { + ok(this.notifyObj.mainActionClicked, "mainAction was clicked"); + ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered"); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + } + }, + { // Test #1 + run: function () { + this.notifyObj = new basicNotification(); + showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + triggerSecondaryCommand(popup, 0); + }, + onHidden: function (popup) { + ok(this.notifyObj.secondaryActionClicked, "secondaryAction was clicked"); + ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered"); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + } + }, + { // Test #2 + run: function () { + this.notifyObj = new basicNotification(); + this.notification = showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + dismissNotification(popup); + }, + onHidden: function (popup) { + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); + this.notification.remove(); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + } + }, + // test opening a notification for a background browser + { // Test #3 + run: function () { + gNewTab = gBrowser.addTab("about:blank"); + isnot(gBrowser.selectedTab, gNewTab, "new tab isn't selected"); + wrongBrowserNotificationObject.browser = gBrowser.getBrowserForTab(gNewTab); + wrongBrowserNotification = showNotification(wrongBrowserNotificationObject); + }, + backgroundShow: function () { + is(PopupNotifications.isPanelOpen, false, "panel isn't open"); + ok(!wrongBrowserNotificationObject.mainActionClicked, "main action wasn't clicked"); + ok(!wrongBrowserNotificationObject.secondaryActionClicked, "secondary action wasn't clicked"); + ok(!wrongBrowserNotificationObject.dismissalCallbackTriggered, "dismissal callback wasn't called"); + } + }, + // now select that browser and test to see that the notification appeared + { // Test #4 + run: function () { + this.oldSelectedTab = gBrowser.selectedTab; + gBrowser.selectedTab = gNewTab; + }, + onShown: function (popup) { + checkPopup(popup, wrongBrowserNotificationObject); + is(PopupNotifications.isPanelOpen, true, "isPanelOpen getter doesn't lie"); + + // switch back to the old browser + gBrowser.selectedTab = this.oldSelectedTab; + }, + onHidden: function (popup) { + // actually remove the notification to prevent it from reappearing + ok(wrongBrowserNotificationObject.dismissalCallbackTriggered, "dismissal callback triggered due to tab switch"); + wrongBrowserNotification.remove(); + ok(wrongBrowserNotificationObject.removedCallbackTriggered, "removed callback triggered"); + wrongBrowserNotification = null; + } + }, + // test that the removed notification isn't shown on browser re-select + { // Test #5 + run: function () { + gBrowser.selectedTab = gNewTab; + }, + updateNotShowing: function () { + is(PopupNotifications.isPanelOpen, false, "panel isn't open"); + gBrowser.removeTab(gNewTab); + } + }, + // Test that two notifications with the same ID result in a single displayed + // notification. + { // Test #6 + run: function () { + this.notifyObj = new basicNotification(); + // Show the same notification twice + this.notification1 = showNotification(this.notifyObj); + this.notification2 = showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + this.notification2.remove(); + }, + onHidden: function (popup) { + ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered"); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + } + }, + // Test that two notifications with different IDs are displayed + { // Test #7 + run: function () { + this.testNotif1 = new basicNotification(); + this.testNotif1.message += " 1"; + showNotification(this.testNotif1); + this.testNotif2 = new basicNotification(); + this.testNotif2.message += " 2"; + this.testNotif2.id += "-2"; + showNotification(this.testNotif2); + }, + onShown: function (popup) { + is(popup.childNodes.length, 2, "two notifications are shown"); + // Trigger the main command for the first notification, and the secondary + // for the second. Need to do mainCommand first since the secondaryCommand + // triggering is async. + triggerMainCommand(popup); + is(popup.childNodes.length, 1, "only one notification left"); + triggerSecondaryCommand(popup, 0); + }, + onHidden: function (popup) { + ok(this.testNotif1.mainActionClicked, "main action #1 was clicked"); + ok(!this.testNotif1.secondaryActionClicked, "secondary action #1 wasn't clicked"); + ok(!this.testNotif1.dismissalCallbackTriggered, "dismissal callback #1 wasn't called"); + + ok(!this.testNotif2.mainActionClicked, "main action #2 wasn't clicked"); + ok(this.testNotif2.secondaryActionClicked, "secondary action #2 was clicked"); + ok(!this.testNotif2.dismissalCallbackTriggered, "dismissal callback #2 wasn't called"); + } + }, + // Test notification without mainAction + { // Test #8 + run: function () { + this.notifyObj = new basicNotification(); + this.notifyObj.mainAction = null; + this.notification = showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + dismissNotification(popup); + }, + onHidden: function (popup) { + this.notification.remove(); + } + }, + // Test two notifications with different anchors + { // Test #9 + run: function () { + this.notifyObj = new basicNotification(); + this.firstNotification = showNotification(this.notifyObj); + this.notifyObj2 = new basicNotification(); + this.notifyObj2.id += "-2"; + this.notifyObj2.anchorID = "addons-notification-icon"; + // Second showNotification() overrides the first + this.secondNotification = showNotification(this.notifyObj2); + }, + onShown: function (popup) { + // This also checks that only one element is shown. + checkPopup(popup, this.notifyObj2); + is(document.getElementById("geo-notification-icon").boxObject.width, 0, + "geo anchor shouldn't be visible"); + dismissNotification(popup); + }, + onHidden: [ + // The second showing triggers a popuphidden event that we should ignore. + function (popup) {}, + function (popup) { + // Remove the notifications + this.firstNotification.remove(); + this.secondNotification.remove(); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + ok(this.notifyObj2.removedCallbackTriggered, "removed callback triggered"); + } + ] + }, + // Test optional params + { // Test #10 + run: function () { + this.notifyObj = new basicNotification(); + this.notifyObj.secondaryActions = undefined; + this.notification = showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + dismissNotification(popup); + }, + onHidden: function (popup) { + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); + this.notification.remove(); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + } + }, + // Test that icons appear + { // Test #11 + run: function () { + this.notifyObj = new basicNotification(); + this.notifyObj.id = "geolocation"; + this.notifyObj.anchorID = "geo-notification-icon"; + // Fake these options (with arbitrary values), + // to avoid strict warnings in SeaMonkey notification constructor. + this.notifyObj.addOptions({ + value: "Learn More...", + href: "http://www.seamonkey-project.org/doc/2.0/geolocation" + }); + this.notification = showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + isnot(document.getElementById("geo-notification-icon").boxObject.width, 0, + "geo anchor should be visible"); + dismissNotification(popup); + }, + onHidden: function (popup) { + let icon = document.getElementById("geo-notification-icon"); + isnot(icon.boxObject.width, 0, + "geo anchor should be visible after dismissal"); + this.notification.remove(); + is(icon.boxObject.width, 0, + "geo anchor should not be visible after removal"); + } + }, + // Test that persistence allows the notification to persist across reloads + { // Test #12 + run: function () { + this.oldSelectedTab = gBrowser.selectedTab; + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + + let self = this; + loadURI("http://example.com/", function() { + self.notifyObj = new basicNotification(); + self.notifyObj.addOptions({ + persistence: 2 + }); + self.notification = showNotification(self.notifyObj); + }); + }, + onShown: function (popup) { + this.complete = false; + + let self = this; + loadURI("http://example.org/", function() { + loadURI("http://example.com/", function() { + + // Next load will remove the notification + self.complete = true; + + loadURI("http://example.org/"); + }); + }); + }, + onHidden: function (popup) { + ok(this.complete, "Should only have hidden the notification after 3 page loads"); + ok(this.notifyObj.removedCallbackTriggered, "removal callback triggered"); + gBrowser.removeTab(gBrowser.selectedTab); + gBrowser.selectedTab = this.oldSelectedTab; + } + }, + // Test that a timeout allows the notification to persist across reloads + { // Test #13 + run: function () { + this.oldSelectedTab = gBrowser.selectedTab; + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + + let self = this; + loadURI("http://example.com/", function() { + self.notifyObj = new basicNotification(); + // Set a timeout of 10 minutes that should never be hit + self.notifyObj.addOptions({ + timeout: Date.now() + 600000 + }); + self.notification = showNotification(self.notifyObj); + }); + }, + onShown: function (popup) { + this.complete = false; + + let self = this; + loadURI("http://example.org/", function() { + loadURI("http://example.com/", function() { + + // Next load will hide the notification + self.notification.options.timeout = Date.now() - 1; + self.complete = true; + + loadURI("http://example.org/"); + }); + }); + }, + onHidden: function (popup) { + ok(this.complete, "Should only have hidden the notification after the timeout was passed"); + this.notification.remove(); + gBrowser.removeTab(gBrowser.selectedTab); + gBrowser.selectedTab = this.oldSelectedTab; + } + }, + // Test that setting persistWhileVisible allows a visible notification to + // persist across location changes + { // Test #14 + run: function () { + this.oldSelectedTab = gBrowser.selectedTab; + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + + let self = this; + loadURI("http://example.com/", function() { + self.notifyObj = new basicNotification(); + self.notifyObj.addOptions({ + persistWhileVisible: true + }); + self.notification = showNotification(self.notifyObj); + }); + }, + onShown: function (popup) { + this.complete = false; + + let self = this; + loadURI("http://example.org/", function() { + loadURI("http://example.com/", function() { + + // Notification should persist across location changes + self.complete = true; + dismissNotification(popup); + }); + }); + }, + onHidden: function (popup) { + ok(this.complete, "Should only have hidden the notification after it was dismissed"); + this.notification.remove(); + gBrowser.removeTab(gBrowser.selectedTab); + gBrowser.selectedTab = this.oldSelectedTab; + } + }, + // Test that nested icon nodes correctly activate popups + { // Test #15 + run: function() { + // Add a temporary box as the anchor with a button + this.box = document.createElement("box"); + PopupNotifications.iconBox.appendChild(this.box); + + let button = document.createElement("button"); + button.setAttribute("label", "Please click me!"); + this.box.appendChild(button); + + // The notification should open up on the box + this.notifyObj = new basicNotification(); + this.notifyObj.anchorID = this.box.id = "nested-box"; + this.notifyObj.addOptions({dismissed: true}); + this.notification = showNotification(this.notifyObj); + + EventUtils.synthesizeMouse(button, 1, 1, {}); + }, + onShown: function(popup) { + checkPopup(popup, this.notifyObj); + dismissNotification(popup); + }, + onHidden: function(popup) { + this.notification.remove(); + this.box.remove(); + } + }, + // Test that popupnotifications without popups have anchor icons shown + { // Test #16 + run: function() { + let notifyObj = new basicNotification(); + notifyObj.anchorID = "geo-notification-icon"; + notifyObj.addOptions({neverShow: true}); + showNotification(notifyObj); + }, + updateNotShowing: function() { + isnot(document.getElementById("geo-notification-icon").boxObject.width, 0, + "geo anchor should be visible"); + } + }, + // Test notification "Not Now" menu item + { // Test #17 + run: function () { + this.notifyObj = new basicNotification(); + this.notification = showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + triggerSecondaryCommand(popup, 1); + }, + onHidden: function (popup) { + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); + this.notification.remove(); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + } + }, + // Test notification close button + { // Test #18 + run: function () { + this.notifyObj = new basicNotification(); + this.notification = showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + let notification = popup.childNodes[0]; + EventUtils.synthesizeMouseAtCenter(notification.closebutton, {}); + }, + onHidden: function (popup) { + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); + this.notification.remove(); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + } + }, + // Test notification when chrome is hidden + { // Test #19 + run: function () { + window.locationbar.visible = false; + this.notifyObj = new basicNotification(); + this.notification = showNotification(this.notifyObj); + window.locationbar.visible = true; + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + is(popup.anchorNode.className, "tabbrowser-tab", "notification anchored to tab"); + dismissNotification(popup); + }, + onHidden: function (popup) { + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); + this.notification.remove(); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + } + }, + // Test notification is removed when dismissed if removeOnDismissal is true + { // Test #20 + run: function () { + this.notifyObj = new basicNotification(); + this.notifyObj.addOptions({ + removeOnDismissal: true + }); + this.notification = showNotification(this.notifyObj); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj); + dismissNotification(popup); + }, + onHidden: function (popup) { + ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered"); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + } + }, + // Test multiple notification icons are shown + { // Test #21 + run: function () { + this.notifyObj1 = new basicNotification(); + this.notifyObj1.id += "_1"; + this.notifyObj1.anchorID = "default-notification-icon"; + this.notification1 = showNotification(this.notifyObj1); + + this.notifyObj2 = new basicNotification(); + this.notifyObj2.id += "_2"; + this.notifyObj2.anchorID = "geo-notification-icon"; + this.notification2 = showNotification(this.notifyObj2); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObj2); + + // check notifyObj1 anchor icon is showing + isnot(document.getElementById("default-notification-icon").boxObject.width, 0, + "default anchor should be visible"); + // check notifyObj2 anchor icon is showing + isnot(document.getElementById("geo-notification-icon").boxObject.width, 0, + "geo anchor should be visible"); + + dismissNotification(popup); + }, + onHidden: [ + function (popup) { + }, + function (popup) { + this.notification1.remove(); + ok(this.notifyObj1.removedCallbackTriggered, "removed callback triggered"); + + this.notification2.remove(); + ok(this.notifyObj2.removedCallbackTriggered, "removed callback triggered"); + } + ] + }, + // Test that multiple notification icons are removed when switching tabs + { // Test #22 + run: function () { + // show the notification on old tab. + this.notifyObjOld = new basicNotification(); + this.notifyObjOld.anchorID = "default-notification-icon"; + this.notificationOld = showNotification(this.notifyObjOld); + + // switch tab + this.oldSelectedTab = gBrowser.selectedTab; + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + + // show the notification on new tab. + this.notifyObjNew = new basicNotification(); + this.notifyObjNew.anchorID = "geo-notification-icon"; + this.notificationNew = showNotification(this.notifyObjNew); + }, + onShown: function (popup) { + checkPopup(popup, this.notifyObjNew); + + // check notifyObjOld anchor icon is removed + is(document.getElementById("default-notification-icon").boxObject.width, 0, + "default anchor shouldn't be visible"); + // check notifyObjNew anchor icon is showing + isnot(document.getElementById("geo-notification-icon").boxObject.width, 0, + "geo anchor should be visible"); + + dismissNotification(popup); + }, + onHidden: [ + function (popup) { + }, + function (popup) { + this.notificationNew.remove(); + gBrowser.removeTab(gBrowser.selectedTab); + + gBrowser.selectedTab = this.oldSelectedTab; + this.notificationOld.remove(); + } + ] + } +]; + +function showNotification(notifyObj) { + return PopupNotifications.show(notifyObj.browser, + notifyObj.id, + notifyObj.message, + notifyObj.anchorID, + notifyObj.mainAction, + notifyObj.secondaryActions, + notifyObj.options); +} + +function checkPopup(popup, notificationObj) { + info("[Test #" + gTestIndex + "] checking popup"); + + ok(notificationObj.shownCallbackTriggered, "shown callback was triggered"); + + let notifications = popup.childNodes; + is(notifications.length, 1, "only one notification displayed"); + let notification = notifications[0]; + let icon = document.getAnonymousElementByAttribute(notification, "class", "popup-notification-icon"); + if (notificationObj.id == "geolocation") { + isnot(icon.boxObject.width, 0, "icon for geo displayed"); + is(popup.anchorNode.className, "notification-anchor-icon", "notification anchored to icon"); + } + is(notification.getAttribute("label"), notificationObj.message, "message matches"); + is(notification.id, notificationObj.id + "-notification", "id matches"); + if (notificationObj.mainAction) { + is(notification.getAttribute("buttonlabel"), notificationObj.mainAction.label, "main action label matches"); + is(notification.getAttribute("buttonaccesskey"), notificationObj.mainAction.accessKey, "main action accesskey matches"); + } + let actualSecondaryActions = notification.childNodes; + let secondaryActions = notificationObj.secondaryActions || []; + let actualSecondaryActionsCount = actualSecondaryActions.length; + if (secondaryActions.length) { + let lastChild = actualSecondaryActions.item(actualSecondaryActions.length - 1); + is(lastChild.tagName, "menuseparator", "menuseparator exists"); + actualSecondaryActionsCount--; + } + is(actualSecondaryActionsCount, secondaryActions.length, actualSecondaryActions.length + " secondary actions"); + secondaryActions.forEach(function (a, i) { + is(actualSecondaryActions[i].getAttribute("label"), a.label, "label for secondary action " + i + " matches"); + is(actualSecondaryActions[i].getAttribute("accesskey"), a.accessKey, "accessKey for secondary action " + i + " matches"); + }); +} + +function triggerMainCommand(popup) { + info("[Test #" + gTestIndex + "] triggering main command"); + let notifications = popup.childNodes; + ok(notifications.length > 0, "at least one notification displayed"); + let notification = notifications[0]; + EventUtils.synthesizeMouseAtCenter(notification.button, {}); +} + +function triggerSecondaryCommand(popup, index) { + info("[Test #" + gTestIndex + "] triggering secondary command"); + let notifications = popup.childNodes; + ok(notifications.length > 0, "at least one notification displayed"); + let notification = notifications[0]; + info("Triggering secondary command for notification " + notification.id); + notification.button.nextSibling.nextSibling.focus(); + + popup.addEventListener("popupshown", function triggerPopupShown() { + popup.removeEventListener("popupshown", triggerPopupShown); + + // Press down until the desired command is selected + for (let i = 0; i <= index; i++) + EventUtils.synthesizeKey("VK_DOWN", {}); + + // Activate + EventUtils.synthesizeKey("VK_RETURN", {}); + }); + + // One down event to open the popup + EventUtils.synthesizeKey("VK_DOWN", { altKey: AppConstants.platform != "macosx" }); +} + +function loadURI(uri, callback) { + if (callback) { + gBrowser.addEventListener("load", function loadURIgBLoad() { + // Ignore the about:blank load + if (gBrowser.currentURI.spec != uri) + return; + + gBrowser.removeEventListener("load", loadURIgBLoad, true); + + callback(); + }, true); + } + gBrowser.loadURI(uri); +} + +function dismissNotification(popup) { + info("[Test #" + gTestIndex + "] dismissing notification"); + executeSoon(function () { + EventUtils.synthesizeKey("VK_ESCAPE", {}); + }); +} diff --git a/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler.js b/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler.js new file mode 100644 index 0000000000..2fc5392dca --- /dev/null +++ b/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler.js @@ -0,0 +1,65 @@ +/* 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 makes sure that the web pages can't register protocol handlers +// inside the private browsing mode. + +function test() { + // initialization + waitForExplicitFinish(); + let windowsToClose = []; + let notificationValue = "Protocol Registration: testprotocol"; + let testURI = "http://example.com/browser/" + + "suite/browser/test/browser/browser_privatebrowsing_protocolhandler_page.html"; + + function doTest(aIsPrivateMode, aWindow, aCallback) { + aWindow.getBrowser().selectedBrowser.addEventListener("load", function onLoad() { + aWindow.getBrowser().selectedBrowser.removeEventListener("load", onLoad, true); + + setTimeout(function() { + let notificationBox = aWindow.getBrowser().getNotificationBox(); + let notification = notificationBox.getNotificationWithValue(notificationValue); + + if (aIsPrivateMode) { + // Make sure the notification is correctly displayed without a remember control + ok(!notification, "Notification box should not be displayed inside of private browsing mode"); + } else { + // Make sure the notification is correctly displayed with a remember control + ok(notification, "Notification box should be displaying outside of private browsing mode"); + } + + aCallback(); + }, 100); // remember control is added in a setTimeout(0) call + }, true); + + aWindow.getBrowser().selectedBrowser.loadURI(testURI); + } + + 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 not on private mode + testOnWindow({}, function(aWin) { + doTest(false, aWin, function() { + // then test when on private mode + testOnWindow({private: true}, function(aWin) { + doTest(true, aWin, finish); + }); + }); + }); +} diff --git a/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler_page.html b/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler_page.html new file mode 100644 index 0000000000..800476fd03 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler_page.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> +<html> + <head> + <title>Protocol registrar page</title> + </head> + <body> + <script> + navigator.registerProtocolHandler("testprotocol", + "https://example.com/foobar?uri=%s", + "Test Protocol"); + </script> + </body> +</html> diff --git a/comm/suite/browser/test/browser/browser_relatedTabs.js b/comm/suite/browser/test/browser/browser_relatedTabs.js new file mode 100644 index 0000000000..f93e212391 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_relatedTabs.js @@ -0,0 +1,52 @@ +/* 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() { + is(getBrowser().tabs.length, 1, "one tab is open initially"); + + // Add several new tabs in sequence, interrupted by selecting a + // different tab, moving a tab around and closing a tab, + // returning a list of opened tabs for verifying the expected order. + // The new tab behaviour is documented in bug 465673 + let tabs = []; + function addTab(aURL, aReferrer) { + tabs.push(getBrowser().addTab(aURL, {referrerURI: aReferrer})); + } + + addTab("http://mochi.test:8888/#0"); + getBrowser().selectedTab = tabs[0]; + addTab("http://mochi.test:8888/#1"); + addTab("http://mochi.test:8888/#2", getBrowser().currentURI); + addTab("http://mochi.test:8888/#3", getBrowser().currentURI); + getBrowser().selectedTab = tabs[tabs.length - 1]; + getBrowser().selectedTab = tabs[0]; + addTab("http://mochi.test:8888/#4", getBrowser().currentURI); + getBrowser().selectedTab = tabs[3]; + addTab("http://mochi.test:8888/#5", getBrowser().currentURI); + getBrowser().removeTab(tabs.pop()); + addTab("about:blank", getBrowser().currentURI); + getBrowser().moveTabTo(getBrowser().selectedTab, 1); + addTab("http://mochi.test:8888/#6", getBrowser().currentURI); + addTab(); + addTab("http://mochi.test:8888/#7"); + + function testPosition(tabNum, expectedPosition, msg) { + is(Array.from(getBrowser().tabs) + .indexOf(tabs[tabNum]), + expectedPosition, + msg); + } + + testPosition(0, 3, "tab without referrer was opened to the far right"); + testPosition(1, 7, "tab without referrer was opened to the far right"); + testPosition(2, 5, "tab with referrer opened immediately to the right"); + testPosition(3, 1, "next tab with referrer opened further to the right"); + testPosition(4, 4, "tab selection changed, tab opens immediately to the right"); + testPosition(5, 6, "blank tab with referrer opens to the right of 3rd original tab where removed tab was"); + testPosition(6, 2, "tab has moved, new tab opens immediately to the right"); + testPosition(7, 8, "blank tab without referrer opens at the end"); + testPosition(8, 9, "tab without referrer opens at the end"); + + tabs.forEach(getBrowser().removeTab, getBrowser()); +} diff --git a/comm/suite/browser/test/browser/browser_scope.js b/comm/suite/browser/test/browser/browser_scope.js new file mode 100644 index 0000000000..e4edac1e04 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_scope.js @@ -0,0 +1,4 @@ +function test() { + ok(!!gBrowser, "gBrowser exists"); + is(gBrowser, getBrowser(), "both ways of getting tabbrowser work"); +} diff --git a/comm/suite/browser/test/browser/browser_selectTabAtIndex.js b/comm/suite/browser/test/browser/browser_selectTabAtIndex.js new file mode 100644 index 0000000000..9da6c8d489 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_selectTabAtIndex.js @@ -0,0 +1,22 @@ +function test() { + for (let i = 0; i < 9; i++) + gBrowser.addTab(); + +/* This part tests accel keys, which are not implemented in Seamonkey yet. + * Commenting out for now ... + * var isLinux = AppConstants.platform == "linux"; + * for (let i = 9; i >= 1; i--) { + * EventUtils.synthesizeKey(i.toString(), { altKey: isLinux, accelKey: !isLinux }); + * + * is(gBrowser.tabContainer.selectedIndex, (i == 9 ? gBrowser.tabs.length : i) - 1, + * (isLinux ? "Alt" : "Accel") + "+" + i + " selects expected tab"); + * } + */ + + gBrowser.selectTabAtIndex(-3); + is(gBrowser.tabContainer.selectedIndex, gBrowser.tabs.length - 3, + "gBrowser.selectTabAtIndex(-3) selects expected tab"); + + for (let i = 0; i < 9; i++) + gBrowser.removeCurrentTab(); +} diff --git a/comm/suite/browser/test/browser/browser_urlbarCopying.js b/comm/suite/browser/test/browser/browser_urlbarCopying.js new file mode 100644 index 0000000000..cc51832996 --- /dev/null +++ b/comm/suite/browser/test/browser/browser_urlbarCopying.js @@ -0,0 +1,204 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const trimPref = "browser.urlbar.trimURLs"; +const phishyUserPassPref = "network.http.phishy-userpass-length"; + +function test() { + + let tab = gBrowser.selectedTab = gBrowser.addTab(); + + registerCleanupFunction(function () { + gBrowser.removeTab(tab); + Services.prefs.clearUserPref(trimPref); + Services.prefs.clearUserPref(phishyUserPassPref); + URLBarSetURI(); + }); + + Services.prefs.setBoolPref(trimPref, true); + Services.prefs.setIntPref(phishyUserPassPref, 32); // avoid prompting about phishing + + waitForExplicitFinish(); + + nextTest(); +} + +var tests = [ + // pageproxystate="invalid" + { + setURL: "http://example.com/", + expectedURL: "example.com", + copyExpected: "example.com" + }, + { + copyVal: "<e>xample.com", + copyExpected: "e" + }, + + // pageproxystate="valid" from this point on (due to the load) + { + loadURL: "http://example.com/", + expectedURL: "example.com", + copyExpected: "http://example.com/" + }, + { + copyVal: "<example.co>m", + copyExpected: "example.co" + }, + { + copyVal: "e<x>ample.com", + copyExpected: "x" + }, + { + copyVal: "<e>xample.com", + copyExpected: "e" + }, + + { + loadURL: "http://example.com/foo", + expectedURL: "example.com/foo", + copyExpected: "http://example.com/foo" + }, + { + copyVal: "<example.com>/foo", + copyExpected: "http://example.com" + }, + { + copyVal: "<example>.com/foo", + copyExpected: "example" + }, + + // Test that userPass is stripped out + { + loadURL: "http://user:pass@mochi.test:8888/browser/browser/base/content/test/general/authenticate.sjs?user=user&pass=pass", + expectedURL: "mochi.test:8888/browser/browser/base/content/test/general/authenticate.sjs?user=user&pass=pass", + copyExpected: "http://mochi.test:8888/browser/browser/base/content/test/general/authenticate.sjs?user=user&pass=pass" + }, + + // Test escaping + { + loadURL: "http://example.com/()%28%29%C3%A9", + expectedURL: "example.com/()()\xe9", + copyExpected: "http://example.com/()%28%29%C3%A9" + }, + { + copyVal: "<example.com/(>)()\xe9", + copyExpected: "http://example.com/(" + }, + { + copyVal: "e<xample.com/(>)()\xe9", + copyExpected: "xample.com/(" + }, + + { + loadURL: "http://example.com/%C3%A9%C3%A9", + expectedURL: "example.com/\xe9\xe9", + copyExpected: "http://example.com/%C3%A9%C3%A9" + }, + { + copyVal: "e<xample.com/\xe9>\xe9", + copyExpected: "xample.com/\xe9" + }, + { + copyVal: "<example.com/\xe9>\xe9", + copyExpected: "http://example.com/\xe9" + }, + + { + loadURL: "http://example.com/?%C3%B7%C3%B7", + expectedURL: "example.com/?\xf7\xf7", + copyExpected: "http://example.com/?%C3%B7%C3%B7" + }, + { + copyVal: "e<xample.com/?\xf7>\xf7", + copyExpected: "xample.com/?\xf7" + }, + { + copyVal: "<example.com/?\xf7>\xf7", + copyExpected: "http://example.com/?\xf7" + }, + + // data: and javsacript: URIs shouldn't be encoded + { + loadURL: "javascript:('%C3%A9%20%25%50')", + expectedURL: "javascript:('%C3%A9 %25P')", + copyExpected: "javascript:('%C3%A9 %25P')" + }, + { + copyVal: "<javascript:(>'%C3%A9 %25P')", + copyExpected: "javascript:(" + }, + + { + loadURL: "data:text/html,(%C3%A9%20%25%50)", + expectedURL: "data:text/html,(%C3%A9 %25P)", + copyExpected: "data:text/html,(%C3%A9 %25P)", + }, + { + copyVal: "<data:text/html,(>%C3%A9 %25P)", + copyExpected: "data:text/html,(" + }, + { + copyVal: "<data:text/html,(%C3%A9 %25P>)", + copyExpected: "data:text/html,(%C3%A9 %25P", + } +]; + +function nextTest() { + let test = tests.shift(); + if (tests.length == 0) + runTest(test, finish); + else + runTest(test, nextTest); +} + +function runTest(test, cb) { + function doCheck() { + if (test.setURL || test.loadURL) { + gURLBar.valueIsTyped = !!test.setURL; + is(gURLBar.textValue, test.expectedURL, "url bar value set"); + } + + testCopy(test.copyVal, test.copyExpected, cb); + } + + if (test.loadURL) { + loadURL(test.loadURL, doCheck); + } else { + if (test.setURL) + gURLBar.value = test.setURL; + doCheck(); + } +} + +function testCopy(copyVal, targetValue, cb) { + info("Expecting copy of: " + targetValue); + waitForClipboard(targetValue, function () { + gURLBar.focus(); + if (copyVal) { + let startBracket = copyVal.indexOf("<"); + let endBracket = copyVal.indexOf(">"); + if (startBracket == -1 || endBracket == -1 || + startBracket > endBracket || + copyVal.replace("<", "").replace(">", "") != gURLBar.textValue) { + ok(false, "invalid copyVal: " + copyVal); + } + gURLBar.selectionStart = startBracket; + gURLBar.selectionEnd = endBracket - 1; + } else { + gURLBar.select(); + } + + goDoCommand("cmd_copy"); + }, cb, cb); +} + +function loadURL(aURL, aCB) { + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + is(gBrowser.currentURI.spec, aURL, "loaded expected URL"); + aCB(); + }, true); + + gBrowser.loadURI(aURL); +} diff --git a/comm/suite/browser/test/browser/feed_tab.html b/comm/suite/browser/test/browser/feed_tab.html new file mode 100644 index 0000000000..50903f48b6 --- /dev/null +++ b/comm/suite/browser/test/browser/feed_tab.html @@ -0,0 +1,17 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=458579 +--> + <head> + <title>Test for page info feeds tab</title> + + <!-- Straight up standard --> + <link rel="alternate" type="application/atom+xml" title="1" href="/1.atom" /> + <link rel="alternate" type="application/rss+xml" title="2" href="/2.rss" /> + <link rel="feed" title="3" href="/3.xml" /> + + </head> + <body> + </body> +</html> diff --git a/comm/suite/browser/test/browser/file_dom_notifications.html b/comm/suite/browser/test/browser/file_dom_notifications.html new file mode 100644 index 0000000000..078c94a42d --- /dev/null +++ b/comm/suite/browser/test/browser/file_dom_notifications.html @@ -0,0 +1,40 @@ +<html> +<head> +<script> +"use strict"; + +function showNotification1() { + var options = { + dir: undefined, + lang: undefined, + body: "Test body", + tag: "Test tag", + icon: undefined, + }; + var n = new Notification("Test title", options); + n.addEventListener("click", function(event) { + event.preventDefault(); + dump("Should focus new window."); + newWindow.focus(); + }); + return n; +} + +function showNotification2() { + var options = { + dir: undefined, + lang: undefined, + body: "Test body", + tag: "Test tag", + icon: undefined, + }; + return new Notification("Test title", options); +} +</script> +</head> +<body> +<form id="notificationForm" onsubmit="showNotification();"> + <input type="submit" value="Show notification" id="submit"/> +</form> +</body> +</html> diff --git a/comm/suite/browser/test/browser/head.js b/comm/suite/browser/test/browser/head.js new file mode 100644 index 0000000000..9a065930a2 --- /dev/null +++ b/comm/suite/browser/test/browser/head.js @@ -0,0 +1,63 @@ +/* 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 findChromeWindowByURI(aURI) { + let windows = Services.wm.getEnumerator(null); + while (windows.hasMoreElements()) { + let win = windows.getNext(); + if (win.location.href == aURI) + return win; + } + return null; +} + +function waitForCondition(condition, nextTest, errorMsg) { + var tries = 0; + var interval = setInterval(function() { + if (tries >= 30) { + ok(false, errorMsg); + moveOn(); + } + if (condition()) { + moveOn(); + } + tries++; + }, 100); + var moveOn = function() { clearInterval(interval); nextTest(); }; +} + +function whenNewWindowLoaded(aOptions, aCallback) { + var { private } = aOptions; + var features = private ? "private,chrome,all,dialog=no" : + "non-private,chrome,all,dialog=no"; + var win = window.openDialog(getBrowserURL(), "_blank", features, + "about:blank"); + win.addEventListener("load", function onLoad() { + win.removeEventListener("load", onLoad); + aCallback(win); + }); +} + +function updateBlocklist(aCallback) { + var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"] + .getService(Ci.nsITimerCallback); + var observer = function() { + Services.obs.removeObserver(observer, "blocklist-updated"); + SimpleTest.executeSoon(aCallback); + }; + Services.obs.addObserver(observer, "blocklist-updated"); + blocklistNotifier.notify(null); +} + +var _originalTestBlocklistURL = null; +function setAndUpdateBlocklist(aURL, aCallback) { + if (!_originalTestBlocklistURL) + _originalTestBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url"); + Services.prefs.setCharPref("extensions.blocklist.url", aURL); + updateBlocklist(aCallback); +} + +function resetBlocklist() { + Services.prefs.setCharPref("extensions.blocklist.url", _originalTestBlocklistURL); +} diff --git a/comm/suite/browser/test/browser/page_style_sample.html b/comm/suite/browser/test/browser/page_style_sample.html new file mode 100644 index 0000000000..633956c4c7 --- /dev/null +++ b/comm/suite/browser/test/browser/page_style_sample.html @@ -0,0 +1,31 @@ +<html> + <head> + <title>Test for page style menu</title> + <!-- data-state values: + 0: should not appear in the page style menu + 0-todo: should not appear in the page style menu, but does + 1: should appear in the page style menu + 2: should appear in the page style menu as the selected stylesheet --> + <link data-state="1" href="404.css" title="1" rel="alternate stylesheet"> + <link data-state="0" title="2" rel="alternate stylesheet"> + <link data-state="0" href="404.css" rel="alternate stylesheet"> + <link data-state="0" href="404.css" title="" rel="alternate stylesheet"> + <link data-state="1" href="404.css" title="3" rel="stylesheet alternate"> + <link data-state="1" href="404.css" title="4" rel=" alternate stylesheet "> + <link data-state="1" href="404.css" title="5" rel="alternate stylesheet"> + <link data-state="2" href="404.css" title="6" rel="stylesheet"> + <link data-state="1" href="404.css" title="7" rel="foo stylesheet"> + <link data-state="0" href="404.css" title="8" rel="alternate"> + <link data-state="1" href="404.css" title="9" rel="alternate STYLEsheet"> + <link data-state="1" href="404.css" title="10" rel="alternate stylesheet" media=""> + <link data-state="1" href="404.css" title="11" rel="alternate stylesheet" media="all"> + <link data-state="1" href="404.css" title="12" rel="alternate stylesheet" media="ALL "> + <link data-state="1" href="404.css" title="13" rel="alternate stylesheet" media="screen"> + <link data-state="1" href="404.css" title="14" rel="alternate stylesheet" media=" Screen"> + <link data-state="1" href="404.css" title="15" rel="alternate stylesheet" media="screen foo"> + <link data-state="1" href="404.css" title="16" rel="alternate stylesheet" media="all screen"> + <link data-state="0-todo" href="404.css" title="17" rel="alternate stylesheet" media="allscreen"> + <link data-state="0-todo" href="404.css" title="18" rel="alternate stylesheet" media="_all"> + </head> + <body></body> +</html> diff --git a/comm/suite/browser/test/browser/redirect_bug623155.sjs b/comm/suite/browser/test/browser/redirect_bug623155.sjs new file mode 100644 index 0000000000..2e4681f240 --- /dev/null +++ b/comm/suite/browser/test/browser/redirect_bug623155.sjs @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const REDIRECT_TO = "https://www.bank1.com/"; // Bad-cert host. + +function handleRequest(aRequest, aResponse) { + // Set HTTP Status + aResponse.setStatusLine(aRequest.httpVersion, 301, "Moved Permanently"); + + // Set redirect URI, mirroring the hash value. + let hash = (/\#.+/.test(aRequest.pathQueryRef))? + "#" + aRequest.pathQueryRef.split("#")[1]: + ""; + aResponse.setHeader("Location", REDIRECT_TO + hash); +} diff --git a/comm/suite/browser/test/browser/title_test.svg b/comm/suite/browser/test/browser/title_test.svg new file mode 100644 index 0000000000..6ab5b2f5c2 --- /dev/null +++ b/comm/suite/browser/test/browser/title_test.svg @@ -0,0 +1,59 @@ +<svg width="640px" height="480px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0"> + <title>This is a root SVG element's title</title> + <foreignObject> + <html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <svg xmlns="http://www.w3.org/2000/svg" id="svg1"> + <title>This is a non-root SVG element title</title> + </svg> + </body> + </html> + </foreignObject> + <text id="text1" x="10px" y="32px" font-size="24px"> + This contains only <title> + <title> + + + This is a title + + </title> + </text> + <text id="text2" x="10px" y="96px" font-size="24px"> + This contains only <desc> + <desc>This is a desc</desc> + </text> + <text id="text3" x="10px" y="128px" font-size="24px"> + This contains nothing. + </text> + <a id="link1" xlink:href="#"> + This link contains <title> + <title> + This is a title + </title> + <text id="text4" x="10px" y="192px" font-size="24px"> + </text> + </a> + <a id="link2" xlink:href="#"> + <text x="10px" y="192px" font-size="24px"> + This text contains <title> + <title> + This is a title + </title> + </text> + </a> + <a id="link3" xlink:href="#" xlink:title="This is an xlink:title attribute"> + <text x="10px" y="224px" font-size="24px"> + This link contains <title> & xlink:title attr. + <title>This is a title</title> + </text> + </a> + <a id="link4" xlink:href="#" xlink:title="This is an xlink:title attribute"> + <text x="10px" y="256px" font-size="24px"> + This link contains xlink:title attr. + </text> + </a> + <text id="text5" x="10px" y="160px" font-size="24px" + xlink:title="This is an xlink:title attribute but it isn't on a link" > + This contains nothing. + </text> +</svg> diff --git a/comm/suite/browser/test/chrome/chrome.ini b/comm/suite/browser/test/chrome/chrome.ini new file mode 100644 index 0000000000..05c31c161a --- /dev/null +++ b/comm/suite/browser/test/chrome/chrome.ini @@ -0,0 +1,3 @@ +[DEFAULT] + +[test_maxSniffing.html] diff --git a/comm/suite/browser/test/chrome/test_maxSniffing.html b/comm/suite/browser/test/chrome/test_maxSniffing.html new file mode 100644 index 0000000000..8fb333e523 --- /dev/null +++ b/comm/suite/browser/test/chrome/test_maxSniffing.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=739040 +--> +<head> + <title>Test that we only sniff 512 bytes</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=739040">Mozilla Bug 739040</a> +<p id="display"> + <iframe id="validTestFrame" src="http://mochi.test:8888/tests/suite/browser/test/mochitest/valid-feed.xml"></iframe> + <iframe id="unsniffableTestFrame" src="http://mochi.test:8888/tests/suite/browser/test/mochitest/valid-unsniffable-feed.xml"></iframe> +</p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody"> + +/** Test for Bug 739040 **/ +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(function() { + is($("validTestFrame").contentDocument.documentElement.id, "feedHandler", + "valid feed should be sniffed"); + isnot($("unsniffableTestFrame").contentDocument.documentElement.id, "feedHandler", + "unsniffable feed should not be sniffed"); +}); +addLoadEvent(SimpleTest.finish); + +</script> +</pre> +</body> +</html> diff --git a/comm/suite/browser/test/mochitest/audio.ogg b/comm/suite/browser/test/mochitest/audio.ogg Binary files differnew file mode 100644 index 0000000000..7e6ef77ec4 --- /dev/null +++ b/comm/suite/browser/test/mochitest/audio.ogg diff --git a/comm/suite/browser/test/mochitest/bug364677-data.xml b/comm/suite/browser/test/mochitest/bug364677-data.xml new file mode 100644 index 0000000000..b48915c050 --- /dev/null +++ b/comm/suite/browser/test/mochitest/bug364677-data.xml @@ -0,0 +1,5 @@ +<rss version="2.0"> + <channel> + <title>t</title> + </channel> +</rss> diff --git a/comm/suite/browser/test/mochitest/bug364677-data.xml^headers^ b/comm/suite/browser/test/mochitest/bug364677-data.xml^headers^ new file mode 100644 index 0000000000..f203c6368e --- /dev/null +++ b/comm/suite/browser/test/mochitest/bug364677-data.xml^headers^ @@ -0,0 +1 @@ +Content-Type: text/xml diff --git a/comm/suite/browser/test/mochitest/bug395533-data.txt b/comm/suite/browser/test/mochitest/bug395533-data.txt new file mode 100644 index 0000000000..e0ed39850f --- /dev/null +++ b/comm/suite/browser/test/mochitest/bug395533-data.txt @@ -0,0 +1,6 @@ +<rss version="2.0"> + <channel> + <link>http://example.org/</link> + <title>t</title> + </channel> +</rss> diff --git a/comm/suite/browser/test/mochitest/bug436801-data.xml b/comm/suite/browser/test/mochitest/bug436801-data.xml new file mode 100644 index 0000000000..0e45c7ed8e --- /dev/null +++ b/comm/suite/browser/test/mochitest/bug436801-data.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<feed xmlns="http://www.w3.org/2005/Atom" xml:base="http://www.example.com/"> + + <title type="xhtml" xml:base="/foo/bar/"> + <div xmlns="http://www.w3.org/1999/xhtml">Example of a <em>special</em> feed (<img height="20px" src="baz.png" alt="base test sprite"/>)</div> + </title> + + <subtitle type="html" xml:base="/foo/bar/"> + <![CDATA[ + With a <em>special</em> subtitle (<img height="20px" src="baz.png" alt="base test sprite"/>) + ]]> + </subtitle> + + <link href="http://example.org/"/> + + <updated>2010-09-02T18:30:02Z</updated> + + <author> + <name>John Doe</name> + </author> + + <id>urn:uuid:22906062-ecbd-46e2-b6a7-3039506a398f</id> + + <entry> + <title type="xhtml" xml:base="/foo/bar/"> + <div xmlns="http://www.w3.org/1999/xhtml">Some <abbr title="Extensible Hyper-text Mark-up Language">XHTML</abbr> examples (<img height="20px" src="baz.png" alt="base test sprite"/>)</div> + </title> + <id>urn:uuid:b48083a7-71a7-4c9c-8515-b7c0d22955e7</id> + <updated>2010-09-02T18:30:02Z</updated> + <summary>Some text.</summary> + </entry> + + <entry> + <title type="html" xml:base="/foo/bar/"> + <![CDATA[ + Some <abbr title="Hyper-text Mark-up Language">HTML</abbr> examples (<img height="20px" src="baz.png" alt="base test sprite"/>) + ]]> + </title> + <id>urn:uuid:1424967a-280a-414d-b0ab-8b11c4ac1bb7</id> + <updated>2010-09-02T18:30:02Z</updated> + <summary>Some text.</summary> + </entry> + +</feed> diff --git a/comm/suite/browser/test/mochitest/ctxmenu-image.png b/comm/suite/browser/test/mochitest/ctxmenu-image.png Binary files differnew file mode 100644 index 0000000000..4c3be50847 --- /dev/null +++ b/comm/suite/browser/test/mochitest/ctxmenu-image.png diff --git a/comm/suite/browser/test/mochitest/feed_discovery.html b/comm/suite/browser/test/mochitest/feed_discovery.html new file mode 100644 index 0000000000..80c35d19ab --- /dev/null +++ b/comm/suite/browser/test/mochitest/feed_discovery.html @@ -0,0 +1,112 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=377611 +--> + <head> + <title>Test for feed discovery</title> + + <!-- Straight up standard --> + <link rel="alternate" type="application/atom+xml" title="1" href="/1.atom" /> + <link rel="alternate" type="application/rss+xml" title="2" href="/2.rss" /> + <link rel="feed" title="3" href="/3.xml" /> + + <!-- rel is a space-separated list --> + <link rel=" alternate " type="application/atom+xml" title="4" href="/4.atom" /> + <link rel="foo alternate" type="application/atom+xml" title="5" href="/5.atom" /> + <link rel="alternate foo" type="application/atom+xml" title="6" href="/6.atom" /> + <link rel="foo alternate foo" type="application/atom+xml" title="7" href="/7.atom" /> + <link rel="meat feed cake" title="8" href="/8.atom" /> + + <!-- rel is case-insensitive --> + <link rel="ALTERNate" type="application/atom+xml" title="9" href="/9.atom" /> + <link rel="fEEd" title="10" href="/10.atom" /> + + <!-- type can have leading and trailing whitespace --> + <link rel="alternate" type=" application/atom+xml " title="11" href="/11.atom" /> + + <!-- type is case-insensitive --> + <link rel="alternate" type="aPPliCAtion/ATom+xML" title="12" href="/12.atom" /> + + <!-- "feed stylesheet" is a feed, though "alternate stylesheet" isn't --> + <link rel="feed stylesheet" title="13" href="/13.atom" /> + + <!-- hyphens or letters around rel not allowed --> + <link rel="disabled-alternate" type="application/atom+xml" title="Bogus1" href="/Bogus1" /> + <link rel="alternates" type="application/atom+xml" title="Bogus2" href="/Bogus2" /> + <link rel=" alternate-like" type="application/atom+xml" title="Bogus3" href="/Bogus3" /> + + <!-- don't tolerate text/xml if title includes 'rss' not as a word --> + <link rel="alternate" type="text/xml" title="Bogus4 scissorsshaped" href="/Bogus4" /> + + <!-- don't tolerate application/xml if title includes 'rss' not as a word --> + <link rel="alternate" type="application/xml" title="Bogus5 scissorsshaped" href="/Bogus5" /> + + <!-- don't tolerate application/rdf+xml if title includes 'rss' not as a word --> + <link rel="alternate" type="application/rdf+xml" title="Bogus6 scissorsshaped" href="/Bogus6" /> + + <!-- don't tolerate random types --> + <link rel="alternate" type="text/plain" title="Bogus7 rss" href="/Bogus7" /> + + <!-- don't find Atom by title --> + <link rel="foopy" type="application/atom+xml" title="Bogus8 Atom and RSS" href="/Bogus8" /> + + <!-- don't find application/rss+xml by title --> + <link rel="goats" type="application/rss+xml" title="Bogus9 RSS and Atom" href="/Bogus9" /> + + <!-- don't find application/rdf+xml by title --> + <link rel="alternate" type="application/rdf+xml" title="Bogus10 RSS and Atom" href="/Bogus10" /> + + <!-- don't find application/xml by title --> + <link rel="alternate" type="application/xml" title="Bogus11 RSS and Atom" href="/Bogus11" /> + + <!-- don't find text/xml by title --> + <link rel="alternate" type="text/xml" title="Bogus12 RSS and Atom" href="/Bogus12" /> + + <!-- alternate and stylesheet isn't a feed --> + <link rel="alternate stylesheet" type="application/rss+xml" title="Bogus13 RSS" href="/Bogus13" /> + </head> + <body> + <script> + window.onload = function() { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + + var tests = new Array(); + + var currentWindow = + window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + var browserSH = currentWindow.XULBrowserWindow; + + var discovered = browserSH.feeds; + tests.push({ check: discovered.length > 0, + message: "some feeds should be discovered" }); + + var feeds = []; + + for (var aFeed of discovered) { + feeds[aFeed.href] = true; + } + + for (var aLink of document.getElementsByTagName("link")) { + // ignore real stylesheets, and anything without an href property + if (aLink.type != "text/css" && aLink.href) { + if (/bogus/i.test(aLink.title)) { + tests.push({ check: !feeds[aLink.href], todo: /todo/i.test(aLink.title), + message: "don't discover " + aLink.href }); + } else { + tests.push({ check: feeds[aLink.href], todo: /todo/i.test(aLink.title), + message: "should discover " + aLink.href }); + } + } + } + window.arguments[0].tests = tests; + window.close(); + } + </script> + </body> +</html> diff --git a/comm/suite/browser/test/mochitest/mochitest.ini b/comm/suite/browser/test/mochitest/mochitest.ini new file mode 100644 index 0000000000..99b25bba9d --- /dev/null +++ b/comm/suite/browser/test/mochitest/mochitest.ini @@ -0,0 +1,17 @@ +[DEFAULT] +support-files = + valid-feed.xml + valid-unsniffable-feed.xml + +[test_bug364677.html] +support-files = bug364677-data.xml bug364677-data.xml^headers^ +[test_bug395533.html] +support-files = bug395533-data.txt +[test_bug436801.html] +support-files = bug436801-data.xml +[test_contextmenu.html] +support-files = audio.ogg ctxmenu-image.png subtst_contextmenu.html video.ogg +skip-if = os != "win" # disabled on Linux due to bug 513558, on Mac after 10.6 due to bug 792304 +[test_feed_discovery.html] +support-files = feed_discovery.html +[test_registerHandler.html] diff --git a/comm/suite/browser/test/mochitest/subtst_contextmenu.html b/comm/suite/browser/test/mochitest/subtst_contextmenu.html new file mode 100644 index 0000000000..63cc70c84e --- /dev/null +++ b/comm/suite/browser/test/mochitest/subtst_contextmenu.html @@ -0,0 +1,70 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Subtest for browser context menu</title> +</head> +<body> +Browser context menu subtest. + +<div id="test-text">Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</div> +<a id="test-link" href="http://mozilla.com">Click the monkey!</a> +<a id="test-mailto" href="mailto:codemonkey@mozilla.com">Mail the monkey!</a><br> +<input id="test-input"><br> +<input id="test-input-select"><br> +<img id="test-image" src="ctxmenu-image.png"> +<canvas id="test-canvas" width="100" height="100" style="background-color: blue"></canvas> +<video controls id="test-video-ok" src="video.ogg" width="100" height="100" style="background-color: green"></video> +<video id="test-audio-in-video" src="audio.ogg" width="100" height="100" style="background-color: red"></video> +<video controls id="test-video-bad" src="bogus.duh" width="100" height="100" style="background-color: orange"></video> +<video controls id="test-video-bad2" width="100" height="100" style="background-color: yellow"> + <source src="bogus.duh" type="video/durrrr;"> +</video> +<iframe id="test-iframe" width="98" height="98" style="border: 1px solid black"></iframe> +<iframe id="test-video-in-iframe" src="video.ogg" width="98" height="98" style="border: 1px solid black"></iframe> +<iframe id="test-image-in-iframe" src="ctxmenu-image.png" width="98" height="98" style="border: 1px solid black"></iframe> +<textarea id="test-textarea">chssseesbbbie</textarea> <!-- a weird word which generates only one suggestion --> +<textarea id="test-textarea-sel">test</textarea> +<div id="test-contenteditable" contenteditable="true">chssseefsbbbie</div> <!-- a more weird word which generates no suggestions --> +<input id="test-input-spellcheck" type="text" spellcheck="true" autofocus value="prodkjfgigrty"> <!-- this one also generates one suggestion --> +<div contextmenu="myMenu"> + <p id="test-pagemenu" hopeless="true">I've got a context menu!</p> + <menu id="myMenu" type="context"> + <menuitem label="Plain item" onclick="document.getElementById('test-pagemenu').removeAttribute('hopeless');"></menuitem> + <menuitem label="Disabled item" disabled></menuitem> + <menuitem> Item w/ textContent</menuitem> + <menu> + <menuitem type="checkbox" label="Checkbox" checked></menuitem> + </menu> + <menu> + <menuitem type="radio" label="Radio1" checked></menuitem> + <menuitem type="radio" label="Radio2"></menuitem> + <menuitem type="radio" label="Radio3"></menuitem> + </menu> + <menu> + <menuitem label="Item w/ icon" icon="favicon.ico"></menuitem> + <menuitem label="Item w/ bad icon" icon="data://www.mozilla.org/favicon.ico"></menuitem> + </menu> + <menu label="Submenu"> + <menuitem type="radio" label="Radio1" radiogroup="rg"></menuitem> + <menuitem type="radio" label="Radio2" checked radiogroup="rg"></menuitem> + <menuitem type="radio" label="Radio3" radiogroup="rg"></menuitem> + <menu> + <menuitem type="checkbox" label="Checkbox"></menuitem> + </menu> + </menu> + <menu hidden> + <menuitem label="Bogus item"></menuitem> + </menu> + <menu> + </menu> + <menuitem label="Hidden item" hidden></menuitem> + <menuitem></menuitem> + </menu> +</div> + +<!-- SeaMonkey specific elements --> +<a href="http://mozilla.com"><img id="test-image-link" alt="Click the monkey!" src="ctxmenu-image.png"></a> +<a href="mailto:codemonkey@mozilla.com"><img id="test-image-mailto" alt="Mail the monkey!" src="ctxmenu-image.png"></a><br> + +</body> +</html> diff --git a/comm/suite/browser/test/mochitest/test_bug364677.html b/comm/suite/browser/test/mochitest/test_bug364677.html new file mode 100644 index 0000000000..e08b1403f6 --- /dev/null +++ b/comm/suite/browser/test/mochitest/test_bug364677.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=364677 +--> +<head> + <title>Test for Bug 364677</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=364677">Mozilla Bug 364677</a> +<p id="display"><iframe id="testFrame" src="bug364677-data.xml"></iframe></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody"> + +/** Test for Bug 364677 **/ +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(function() { + is(SpecialPowers.wrap($("testFrame")).contentDocument.documentElement.id, "feedHandler", + "Feed served as text/xml without a channel/link should have been sniffed"); +}); +addLoadEvent(SimpleTest.finish); +</script> +</pre> +</body> +</html> + diff --git a/comm/suite/browser/test/mochitest/test_bug395533.html b/comm/suite/browser/test/mochitest/test_bug395533.html new file mode 100644 index 0000000000..5d3cfa0121 --- /dev/null +++ b/comm/suite/browser/test/mochitest/test_bug395533.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=395533 +--> +<head> + <title>Test for Bug 395533</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=395533">Mozilla Bug 395533</a> +<p id="display"><iframe id="testFrame" src="bug395533-data.txt"></iframe></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody"> + +/** Test for Bug 395533 **/ +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(function() { + // Need privs because the feed seems to have an about:feeds principal or some + // such. It's not same-origin with us in any case. + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + is($("testFrame").contentDocument.documentElement.id, "", + "Text got sniffed as a feed?"); +}); +addLoadEvent(SimpleTest.finish); + + + + +</script> +</pre> +</body> +</html> + diff --git a/comm/suite/browser/test/mochitest/test_bug436801.html b/comm/suite/browser/test/mochitest/test_bug436801.html new file mode 100644 index 0000000000..6ca1b83cf5 --- /dev/null +++ b/comm/suite/browser/test/mochitest/test_bug436801.html @@ -0,0 +1,118 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=436801 +--> +<head> + <title>Test feed preview subscribe UI</title> + <script src="/MochiKit/packed.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436801">Mozilla Bug 436801</a> +<p id="display"><iframe id="testFrame" src="bug436801-data.xml"></iframe></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +<script class="testbody"> + +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(function () { + var doc = SpecialPowers.wrap($("testFrame")).contentDocument; + + checkNode(doc.getElementById("feedTitleText"), [ + "ELEMENT", "h1", { "xml:base": "http://www.example.com/foo/bar/" }, [ + ["TEXT", "Example of a "], + ["ELEMENT", "em", [ + ["TEXT", "special"], + ]], + ["TEXT", " feed ("], + ["ELEMENT", "img", { "src": "baz.png" }], + ["TEXT", ")"], + ] + ]); + + checkNode(doc.getElementById("feedSubtitleText"), [ + "ELEMENT", "h2", { "xml:base": "http://www.example.com/foo/bar/" }, [ + ["TEXT", "With a "], + ["ELEMENT", "em", [ + ["TEXT", "special"], + ]], + ["TEXT", " subtitle ("], + ["ELEMENT", "img", { "src": "baz.png" }], + ["TEXT", ")"], + ] + ]); + + checkNode(doc.querySelector(".entry").firstChild.firstChild.firstChild, [ + "ELEMENT", "span", { "xml:base": "http://www.example.com/foo/bar/" }, [ + ["TEXT", "Some "], + ["ELEMENT", "abbr", { title: "Extensible Hyper-text Mark-up Language" }, [ + ["TEXT", "XHTML"], + ]], + ["TEXT", " examples ("], + ["ELEMENT", "img", { "src": "baz.png" }], + ["TEXT", ")"], + ] + ]); + + checkNode(doc.querySelectorAll(".entry")[1].firstChild.firstChild.firstChild, [ + "ELEMENT", "span", { "xml:base": "http://www.example.com/foo/bar/" }, [ + ["TEXT", "Some "], + ["ELEMENT", "abbr", { title: "Hyper-text Mark-up Language" }, [ + ["TEXT", "HTML"], + ]], + ["TEXT", " examples ("], + ["ELEMENT", "img", { "src": "baz.png" }], + ["TEXT", ")"], + ] + ]); +}); + +addLoadEvent(SimpleTest.finish); + +function checkNode(node, schema) { + var typeName = schema.shift() + "_NODE"; + var type = Node[typeName]; + is(node.nodeType, type, "Node should be expected type " + typeName); + if (type == Node.TEXT_NODE) { + var text = schema.shift(); + is(node.data, text, "Text should match"); + return; + } + // type == Node.ELEMENT_NODE + var tag = schema.shift(); + is(node.localName, tag, "Element should have expected tag"); + while (schema.length) { + var val = schema.shift(); + if (Array.isArray(val)) + var childSchema = val; + else + var attrSchema = val; + } + if (attrSchema) { + var nsTable = { + xml: "http://www.w3.org/XML/1998/namespace", + }; + for (var name in attrSchema) { + var [ns, nsName] = name.split(":"); + var val = nsName ? node.getAttributeNS(nsTable[ns], nsName) : + node.getAttribute(name); + is(val, attrSchema[name], "Attribute " + name + " should match"); + } + } + if (childSchema) { + var numChildren = node.childNodes.length; + is(childSchema.length, numChildren, + "Element should have expected number of children"); + for (var i = 0; i < numChildren; i++) + checkNode(node.childNodes[i], childSchema[i]); + } +} + +</script> +</pre> +</body> +</html> diff --git a/comm/suite/browser/test/mochitest/test_contextmenu.html b/comm/suite/browser/test/mochitest/test_contextmenu.html new file mode 100644 index 0000000000..0dea49c925 --- /dev/null +++ b/comm/suite/browser/test/mochitest/test_contextmenu.html @@ -0,0 +1,931 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Tests for browser context menu</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +Browser context menu tests. +<p id="display"></p> + +<div id="content"> +</div> + +<pre id="test"> +<script class="testbody"> + +/** Test for Login Manager: multiple login autocomplete. **/ + +SpecialPowers.ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm", window); +SpecialPowers.ChromeUtils.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window); + +const Cc = SpecialPowers.Cc; +const Ci = SpecialPowers.Ci; + +function openContextMenuFor(element, shiftkey, waitForSpellCheck) { + // Context menu should be closed before we open it again. + is(SpecialPowers.wrap(contextMenu).state, "closed", "checking if popup is closed"); + + if (lastElement) + lastElement.blur(); + element.focus(); + + // Some elements need time to focus and spellcheck before any tests are + // run on them. + function actuallyOpenContextMenuFor() { + lastElement = element; + var eventDetails = { type : "contextmenu", button : 2, shiftKey : shiftkey }; + synthesizeMouse(element, 2, 2, eventDetails, element.ownerDocument.defaultView); + } + + if (waitForSpellCheck) + onSpellCheck(element, actuallyOpenContextMenuFor); + else + actuallyOpenContextMenuFor(); +} + +function closeContextMenu() { + contextMenu.hidePopup(); +} + +function executeCopyCommand(command, expectedValue) +{ + // Just execute the command directly rather than simulating a context menu + // press to avoid having to deal with its asynchronous nature + SpecialPowers.wrap(subwindow).controllers.getControllerForCommand(command).doCommand(command); + + // The easiest way to check the clipboard is to paste the contents into a + // textbox + input.focus(); + input.value = ""; + SpecialPowers.wrap(input).controllers.getControllerForCommand("cmd_paste").doCommand("cmd_paste"); + is(input.value, expectedValue, "paste for command " + command); +} + +function invokeItemAction(generatedItemId) +{ + var item = contextMenu.getElementsByAttribute("generateditemid", + generatedItemId)[0]; + ok(item, "Got generated XUL menu item"); + item.doCommand(); + is(pagemenu.hasAttribute("hopeless"), false, "attribute got removed"); +} + +function getVisibleMenuItems(aMenu, aData) { + var items = []; + var accessKeys = {}; + for (var i = 0; i < aMenu.childNodes.length; i++) { + var item = aMenu.childNodes[i]; + if (item.hidden) + continue; + + var key = item.accessKey; + if (key) + key = key.toLowerCase(); + + var isGenerated = item.hasAttribute("generateditemid"); + + if (item.nodeName == "menuitem") { + var isSpellSuggestion = item.className == "spell-suggestion"; + if (isSpellSuggestion) { + is(item.id, "", "child menuitem #" + i + " is a spelling suggestion"); + } else if (isGenerated) { + is(item.id, "", "child menuitem #" + i + " is a generated item"); + } else { + ok(item.id, "child menuitem #" + i + " has an ID"); + } + var label = item.getAttribute("label"); + ok(label.length, "menuitem " + item.id + " has a label"); + if (isSpellSuggestion) { + is(key, "", "Spell suggestions shouldn't have an access key"); + items.push("*" + label); + } else if (isGenerated) { + items.push("+" + label); + } else if (item.id.indexOf("spell-check-dictionary-") != 0 && + item.id != "spell-no-suggestions") { + ok(key, "menuitem " + item.id + " has an access key"); + if (accessKeys[key]) + ok(false, "menuitem " + item.id + " has same accesskey as " + accessKeys[key]); + else + accessKeys[key] = item.id; + } + if (!isSpellSuggestion && !isGenerated) { + items.push(item.id); + } + if (isGenerated) { + var p = {}; + p.type = item.getAttribute("type"); + p.icon = item.getAttribute("image"); + p.checked = item.hasAttribute("checked"); + p.disabled = item.hasAttribute("disabled"); + items.push(p); + } else { + items.push(!item.disabled); + } + } else if (item.nodeName == "menuseparator") { + ok(true, "--- seperator id is " + item.id); + items.push("---"); + items.push(null); + } else if (item.nodeName == "menu") { + if (isGenerated) { + item.id = "generated-submenu-" + aData.generatedSubmenuId++; + } + ok(item.id, "child menu #" + i + " has an ID"); + if (!isGenerated) { + ok(key, "menu has an access key"); + if (accessKeys[key]) + ok(false, "menu " + item.id + " has same accesskey as " + accessKeys[key]); + else + accessKeys[key] = item.id; + } + items.push(item.id); + items.push(!item.disabled); + // Add a dummy item to that the indexes in checkMenu are the same + // for expectedItems and actualItems. + items.push([]); + items.push(null); + } else { + ok(false, "child #" + i + " of menu ID " + aMenu.id + + " has an unknown type (" + item.nodeName + ")"); + } + } + return items; +} + +function checkContextMenu(expectedItems) { + is(contextMenu.state, "open", "checking if popup is open"); + var data = { generatedSubmenuId: 1 }; + checkMenu(contextMenu, expectedItems, data); +} + +/* + * checkMenu - checks to see if the specified <menupopup> contains the + * expected items and state. + * expectedItems is a array of (1) item IDs and (2) a boolean specifying if + * the item is enabled or not (or null to ignore it). Submenus can be checked + * by providing a nested array entry after the expected <menu> ID. + * For example: ["blah", true, // item enabled + * "submenu", null, // submenu + * ["sub1", true, // submenu contents + * "sub2", false], null, // submenu contents + * "lol", false] // item disabled + * + */ +function checkMenu(menu, expectedItems, data) { + var actualItems = getVisibleMenuItems(menu, data); + //ok(false, "Items are: " + actualItems); + for (var i = 0; i < expectedItems.length; i+=2) { + var actualItem = actualItems[i]; + var actualEnabled = actualItems[i + 1]; + var expectedItem = expectedItems[i]; + var expectedEnabled = expectedItems[i + 1]; + if (expectedItem instanceof Array) { + ok(true, "Checking submenu..."); + var menuID = expectedItems[i - 2]; // The last item was the menu ID. + var submenu = menu.getElementsByAttribute("id", menuID)[0]; + ok(submenu && submenu.nodeName == "menu", "got expected submenu element"); + checkMenu(submenu.menupopup, expectedItem, data); + } else { + is(actualItem, expectedItem, + "checking item #" + i/2 + " (" + expectedItem + ") name"); + + if (typeof expectedEnabled == "object" && expectedEnabled != null || + typeof actualEnabled == "object" && actualEnabled != null) { + + ok(!(actualEnabled == null), "actualEnabled is not null"); + ok(!(expectedEnabled == null), "expectedEnabled is not null"); + is(typeof actualEnabled, typeof expectedEnabled, "checking types"); + + if (typeof actualEnabled != typeof expectedEnabled || + actualEnabled == null || expectedEnabled == null) + continue; + + is(actualEnabled.type, expectedEnabled.type, + "checking item #" + i/2 + " (" + expectedItem + ") type attr value"); + var icon = actualEnabled.icon; + if (icon) { + var tmp = ""; + var j = icon.length - 1; + while (j && icon[j] != "/") { + tmp = icon[j--] + tmp; + } + icon = tmp; + } + is(icon, expectedEnabled.icon, + "checking item #" + i/2 + " (" + expectedItem + ") icon attr value"); + is(actualEnabled.checked, expectedEnabled.checked, + "checking item #" + i/2 + " (" + expectedItem + ") has checked attr"); + is(actualEnabled.disabled, expectedEnabled.disabled, + "checking item #" + i/2 + " (" + expectedItem + ") has disabled attr"); + } else if (expectedEnabled != null) + is(actualEnabled, expectedEnabled, + "checking item #" + i/2 + " (" + expectedItem + ") enabled state"); + } + } + // Could find unexpected extra items at the end... + is(actualItems.length, expectedItems.length, "checking expected number of menu entries"); +} + +/* + * runTest + * + * Called by a popupshowing event handler. Each test checks for expected menu + * contents, closes the popup, and finally triggers the popup on a new element + * (thus kicking off another cycle). + * + */ +function runTest(testNum) { + ok(true, "Starting test #" + testNum); + + switch (testNum) { + case 1: + // Invoke context menu for next test. + openContextMenuFor(text); + break; + + case 2: + // Context menu for plain text + plainTextItems = ["context-back", false, + "context-forward", false, + "context-reload", true, + "context-stop", false, + "---", null, + "context-bookmarkpage", true, + "context-savepage", true, + "context-sendpage", true, + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "---", null, + "context-viewsource", true, + "context-viewinfo", true]; + checkContextMenu(plainTextItems); + closeContextMenu(); + openContextMenuFor(link); // Invoke context menu for next test. + break; + + case 3: + // Context menu for text link + checkContextMenu(["context-openlinkintab", true, + "context-openlink", true, + "context-openlinkinprivatewindow", true, + "---", null, + "context-bookmarklink", true, + "context-savelink", true, + "context-sendlink", true, + "context-copylink", true, + "---", null, + "context-bookmarkpage", true, + "context-savepage", true, + "---", null, + "context-metadata", true]); + closeContextMenu(); + openContextMenuFor(mailto); // Invoke context menu for next test. + break; + + case 4: + // Context menu for text mailto-link + checkContextMenu(["context-copyemail", true, + "context-copylink", true, + "---", null, + "context-bookmarkpage", true, + "context-savepage", true, + "---", null, + "context-metadata", true]); + closeContextMenu(); + openContextMenuFor(input); // Invoke context menu for next test. + break; + + case 5: + // Context menu for text input field + checkContextMenu(["context-undo", false, + "context-redo", false, + "---", null, + "context-cut", false, + "context-copy", false, + "context-paste", null, // ignore clipboard state + "context-delete", false, + "---", null, + "context-selectall", false, + "---", null, + "spell-check-enabled", true]); + closeContextMenu(); + input_sel.value = "test"; + input_sel.select(); + openContextMenuFor(input_sel); // Invoke context menu for next test. + break; + + case 6: + // Context menu for text input field with text + checkContextMenu(["context-undo", false, + "context-redo", false, + "---", null, + "context-cut", true, + "context-copy", true, + "context-paste", null, // ignore clipboard state + "context-delete", true, + "---", null, + "context-selectall", true, + "context-searchselect", true, + "---", null, + "spell-check-enabled", true]); + closeContextMenu(); + openContextMenuFor(img); // Invoke context menu for next test. + break; + + case 7: + // Context menu for an image + checkContextMenu(["context-reloadimage", true, + "context-viewimage", true, + "context-blockimage", true, + "context-copyimage", true, + "---", null, + "context-saveimage", true, + "context-sendimage", true].concat( + ("@mozilla.org/suite/shell-service;1" in Cc) ? + ["context-setDesktopBackground", true] : []).concat( + ["---", null, + "context-bookmarkpage", true, + "context-savepage", true, + "context-sendpage", true, + "---", null, + "context-metadata", true])); + closeContextMenu(); + openContextMenuFor(canvas); // Invoke context menu for next test. + break; + + case 8: + // Context menu for a canvas + checkContextMenu(["context-viewimage", true, + "context-saveimage", true, + "context-bookmarkpage", true, + "context-sendpage", true, + "context-selectall", true]); + closeContextMenu(); + openContextMenuFor(video_ok); // Invoke context menu for next test. + break; + + case 9: + // Context menu for a video (with a VALID media source) + checkContextMenu(["context-media-play", true, + "context-media-mute", true, + "context-media-playbackrate", true, + ["context-media-playbackrate-050", true, + "context-media-playbackrate-100", true, + "context-media-playbackrate-150", true, + "context-media-playbackrate-200", true], null, + "context-media-hidecontrols", true, + "context-video-showstats", true, + "context-video-fullscreen", true, + "---", null, + "context-viewvideo", true, + "context-copyvideourl", true, + "---", null, + "context-savevideo", true, + "context-sendvideo", true, + "context-video-saveimage", true]); + closeContextMenu(); + openContextMenuFor(audio_in_video); // Invoke context menu for next test. + break; + + case 10: + // Context menu for a video (with an audio-only file) + checkContextMenu(["context-media-play", true, + "context-media-mute", true, + "context-media-playbackrate", true, + ["context-media-playbackrate-050", true, + "context-media-playbackrate-100", true, + "context-media-playbackrate-150", true, + "context-media-playbackrate-200", true], null, + "context-media-showcontrols", true, + "---", null, + "context-copyaudiourl", true, + "---", null, + "context-saveaudio", true, + "context-sendaudio", true]); + closeContextMenu(); + openContextMenuFor(video_bad); // Invoke context menu for next test. + break; + + case 11: + // Context menu for a video (with an INVALID media source) + checkContextMenu(["context-media-play", false, + "context-media-mute", false, + "context-media-playbackrate", false, + ["context-media-playbackrate-050", null, + "context-media-playbackrate-100", null, + "context-media-playbackrate-150", null, + "context-media-playbackrate-200", null], null, + "context-media-hidecontrols", false, + "context-video-showstats", false, + "context-video-fullscreen", false, + "---", null, + "context-viewvideo", true, + "context-copyvideourl", true, + "---", null, + "context-savevideo", true, + "context-sendvideo", true, + "context-video-saveimage", false]); + closeContextMenu(); + openContextMenuFor(video_bad2); // Invoke context menu for next test. + break; + + case 12: + // Context menu for a video (with an INVALID media source) + checkContextMenu(["context-media-play", false, + "context-media-mute", false, + "context-media-playbackrate", false, + ["context-media-playbackrate-050", null, + "context-media-playbackrate-100", null, + "context-media-playbackrate-150", null, + "context-media-playbackrate-200", null], null, + "context-media-hidecontrols", false, + "context-video-showstats", false, + "context-video-fullscreen", false, + "---", null, + "context-viewvideo", false, + "context-copyvideourl", false, + "---", null, + "context-savevideo", false, + "context-sendvideo", false, + "context-video-saveimage", false]); + closeContextMenu(); + openContextMenuFor(iframe); // Invoke context menu for next test. + break; + + case 13: + // Context menu for an iframe + checkContextMenu(["context-back", false, + "context-forward", false, + "context-reload", true, + "context-stop", false, + "---", null, + "context-bookmarkpage", true, + "context-savepage", true, + "context-sendpage", true, + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "---", null, + "context-viewsource", true, + "context-viewinfo", true, + "---", null, + "frame", null, + ["context-showonlythisframe", true, + "context-openframeintab", true, + "context-openframe", true, + "---", null, + "context-reloadframe", true, + "---", null, + "context-bookmarkframe", true, + "context-saveframe", true, + "context-sendframe", true, + "---", null, + "context-viewframesource", true, + "context-viewframeinfo", true], null]); + closeContextMenu(); + openContextMenuFor(video_in_iframe); // Invoke context menu for next test. + break; + + case 14: + // Context menu for a video in an iframe + checkContextMenu(["context-media-play", true, + "context-media-mute", true, + "context-media-playbackrate", true, + ["context-media-playbackrate-050", true, + "context-media-playbackrate-100", true, + "context-media-playbackrate-150", true, + "context-media-playbackrate-200", true], null, + "context-media-hidecontrols", true, + "context-video-showstats", true, + "context-video-fullscreen", true, + "---", null, + "context-viewvideo", true, + "context-copyvideourl", true, + "---", null, + "context-savevideo", true, + "context-sendvideo", true, + "context-video-saveimage", true, + "---", null, + "frame", null, + ["context-showonlythisframe", true, + "context-openframeintab", true, + "context-openframe", true, + "---", null, + "context-reloadframe", true, + "---", null, + "context-bookmarkframe", true, + "context-saveframe", true, + "context-sendframe", true, + "---", null, + "context-viewframesource", true, + "context-viewframeinfo", true], null]); + closeContextMenu(); + openContextMenuFor(image_in_iframe); // Invoke context menu for next test. + break; + + case 15: + // Context menu for an image in an iframe + checkContextMenu(["context-reloadimage", true, + "context-viewimage", true, + "context-blockimage", true, + "context-copyimage", true, + "---", null, + "context-saveimage", true, + "context-sendimage", true, + ].concat( + ("@mozilla.org/suite/shell-service;1" in Cc) ? + ["context-setDesktopBackground", true] : []) + .concat( + ["---", null, + "context-sendpage", true, + "---", null, + "context-metadata", true, + "---", null, + "frame", null, + ["context-showonlythisframe", true, + "context-openframeintab", true, + "context-openframe", true, + "---", null, + "context-reloadframe", true, + "---", null, + "context-bookmarkframe", true, + "context-saveframe", true, + "context-sendframe", true, + "---", null, + "context-viewframesource", true, + "context-viewframeinfo", true], null])); + closeContextMenu(); + openContextMenuFor(text); // Invoke context menu for next test. + break; + + case 16: + // Re-check context menu for plain text to make sure it hasn't changed + checkContextMenu(plainTextItems); + closeContextMenu(); + textarea_sel.select(); + openContextMenuFor(textarea_sel, false, true); + break; + + case 17: + // search for text with text area's selected value + checkContextMenu(["context-undo", false, + "context-redo", false, + "---", null, + "context-cut", true, + "context-copy", true, + "context-paste", null, // ignore clipboard state + "context-delete", true, + "---", null, + "context-selectall", true, + "context-searchselect", true, + "---", null, + "spell-check-enabled", true, + "spell-dictionaries", true, + ["spell-check-dictionary-en-US", true, + "---", null, + "spell-add-dictionaries", true], null]); + closeContextMenu(); + openContextMenuFor(textarea, false, true); // Invoke context menu for next test, but wait for the spellcheck. + break; + + case 18: + // Context menu for textarea + checkContextMenu(["*chubbiness", true, // spelling suggestion + "---", null, + "spell-add-to-dictionary", true, + "spell-ignore-word", true, + "---", null, + "context-undo", false, + "context-redo", false, + "---", null, + "context-cut", false, + "context-copy", false, + "context-paste", null, // ignore clipboard state + "context-delete", false, + "---", null, + "context-selectall", true, + "---", null, + "spell-check-enabled", true, + "spell-dictionaries", true, + ["spell-check-dictionary-en-US", true, + "---", null, + "spell-add-dictionaries", true], null]); + + contextMenu.ownerDocument.getElementById("spell-add-to-dictionary").doCommand(); // Add to dictionary + closeContextMenu(); + openContextMenuFor(textarea, false, true); // Invoke context menu for next test. + break; + + case 19: + // Context menu for textarea after a word has been added + // to the dictionary + checkContextMenu(["spell-undo-add-to-dictionary", true, + "---", null, + "context-undo", false, + "context-redo", false, + "---", null, + "context-cut", false, + "context-copy", false, + "context-paste", null, // ignore clipboard state + "context-delete", false, + "---", null, + "context-selectall", true, + "---", null, + "spell-check-enabled", true, + "spell-dictionaries", true, + ["spell-check-dictionary-en-US", true, + "---", null, + "spell-add-dictionaries", true], null, + ]); + + contextMenu.ownerDocument.getElementById("spell-undo-add-to-dictionary").doCommand(); // Undo add to dictionary + closeContextMenu(); + openContextMenuFor(contenteditable, false, true); + break; + + case 20: + // Context menu for contenteditable + checkContextMenu(["spell-no-suggestions", false, + "---", null, + "spell-add-to-dictionary", true, + "spell-ignore-word", true, + "---", null, + "context-undo", false, + "context-redo", false, + "---", null, + "context-cut", false, + "context-copy", false, + "context-paste", null, // ignore clipboard state + "context-delete", false, + "---", null, + "context-selectall", true, + "---", null, + "spell-check-enabled", true, + "spell-dictionaries", true, + ["spell-check-dictionary-en-US", true, + "---", null, + "spell-add-dictionaries", true], null]); + + closeContextMenu(); + openContextMenuFor(inputspell, false, true); // Invoke context menu for next test. + break; + + case 21: + // Context menu for spell-check input + checkContextMenu(["*prodigality", true, // spelling suggestion + "---", null, + "spell-add-to-dictionary", true, + "spell-ignore-word", true, + "---", null, + "context-undo", false, + "context-redo", false, + "---", null, + "context-cut", false, + "context-copy", false, + "context-paste", null, // ignore clipboard state + "context-delete", false, + "---", null, + "context-selectall", true, + "---", null, + "spell-check-enabled", true, + "spell-dictionaries", true, + ["spell-check-dictionary-en-US", true, + "---", null, + "spell-add-dictionaries", true], null]); + + closeContextMenu(); + openContextMenuFor(link); // Invoke context menu for next test. + break; + + case 22: + executeCopyCommand("cmd_copyLink", "http://mozilla.com/"); + closeContextMenu(); + openContextMenuFor(pagemenu); // Invoke context menu for next test. + break; + + case 23: + // Context menu for element with assigned content context menu + checkContextMenu(["+Plain item", {type: "", icon: "", checked: false, disabled: false}, + "+Disabled item", {type: "", icon: "", checked: false, disabled: true}, + "+Item w/ textContent", {type: "", icon: "", checked: false, disabled: false}, + "---", null, + "+Checkbox", {type: "checkbox", icon: "", checked: true, disabled: false}, + "---", null, + "+Radio1", {type: "checkbox", icon: "", checked: true, disabled: false}, + "+Radio2", {type: "checkbox", icon: "", checked: false, disabled: false}, + "+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false}, + "---", null, + "+Item w/ icon", {type: "", icon: "favicon.ico", checked: false, disabled: false}, + "+Item w/ bad icon", {type: "", icon: "", checked: false, disabled: false}, + "---", null, + "generated-submenu-1", true, + ["+Radio1", {type: "checkbox", icon: "", checked: false, disabled: false}, + "+Radio2", {type: "checkbox", icon: "", checked: true, disabled: false}, + "+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false}, + "---", null, + "+Checkbox", {type: "checkbox", icon: "", checked: false, disabled: false}], null, + "---", null, + "context-back", false, + "context-forward", false, + "context-reload", true, + "context-stop", false, + "---", null, + "context-bookmarkpage", true, + "context-savepage", true, + "context-sendpage", true, + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "---", null, + "context-viewsource", true, + "context-viewinfo", true]); + + invokeItemAction("0"); + closeContextMenu(); + openContextMenuFor(pagemenu, true); // Invoke context menu for next test. + break; + + case 24: + // Context menu for element with assigned content context menu + // The shift key should bypass content context menu processing + checkContextMenu(["context-back", false, + "context-forward", false, + "context-reload", true, + "context-stop", false, + "---", null, + "context-bookmarkpage", true, + "context-savepage", true, + "context-sendpage", true, + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "---", null, + "context-viewsource", true, + "context-viewinfo", true]); + closeContextMenu(); + + // Continue with SeaMonkey specific cases. + openContextMenuFor(img_link); // Invoke context menu for next test. + break; + + case 25: + // Context menu for an image with a link + checkContextMenu(["context-openlinkintab", true, + "context-openlink", true, + "context-openlinkinprivatewindow", true, + "---", null, + "context-bookmarklink", true, + "context-savelink", true, + "context-sendlink", true, + "context-copylink", true, + "---", null, + "context-reloadimage", true, + "context-viewimage", true, + "context-blockimage", true, + "context-copyimage", true, + "---", null, + "context-saveimage", true, + "context-sendimage", true].concat( + ("@mozilla.org/suite/shell-service;1" in Cc) ? + ["context-setDesktopBackground", true] : []).concat( + ["---", null, + "context-bookmarkpage", true, + "---", null, + "context-metadata", true])); + closeContextMenu(); + openContextMenuFor(img_mailto); // Invoke context menu for next test. + break; + + case 26: + // Context menu for an image with a mailto: link + checkContextMenu(["context-copyemail", true, + "context-copylink", true, + "---", null, + "context-reloadimage", true, + "context-viewimage", true, + "context-blockimage", true, + "context-copyimage", true, + "---", null, + "context-saveimage", true, + "context-sendimage", true].concat( + ("@mozilla.org/suite/shell-service;1" in Cc) ? + ["context-setDesktopBackground", true] : []).concat( + ["---", null, + "context-bookmarkpage", true, + "---", null, + "context-metadata", true])); + closeContextMenu(); + + subwindow.close(); + SimpleTest.finish(); + return; + + /* + * Other things that would be nice to test: + * - selected text + * - spelling / misspelled word (in text input?) + * - check state of disabled items + * - test execution of menu items (maybe as a separate test?) + */ + + default: + ok(false, "Unexpected invocation of test #" + testNum); + subwindow.close(); + SimpleTest.finish(); + return; + } + +} + + +var testNum = 1; +var subwindow, chromeWin, contextMenu, lastElement; +var text, link, mailto, input, img, canvas, video_ok, video_bad, video_bad2, + iframe, video_in_iframe, image_in_iframe, textarea, contenteditable, + inputspell, pagemenu, audio_in_video; + +// SeaMonkey specific variables. +var img_link, img_mailto; + +function startTest() { + chromeWin = SpecialPowers.wrap(subwindow) + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow) + .QueryInterface(Ci.nsIDOMChromeWindow); + contextMenu = chromeWin.document.getElementById("contentAreaContextMenu"); + ok(contextMenu, "Got context menu XUL"); + + if (chromeWin.document.getElementById("context-stop").getAttribute("disabled") != "true") { + isnot(false, "Document still loading"); + SimpleTest.executeSoon(startTest); + return; + } + + lastElement = null; + + text = subwindow.document.getElementById("test-text"); + link = subwindow.document.getElementById("test-link"); + mailto = subwindow.document.getElementById("test-mailto"); + input = subwindow.document.getElementById("test-input"); + input_sel = subwindow.document.getElementById("test-input-select"); + img = subwindow.document.getElementById("test-image"); + canvas = subwindow.document.getElementById("test-canvas"); + video_ok = subwindow.document.getElementById("test-video-ok"); + audio_in_video = subwindow.document.getElementById("test-audio-in-video"); + video_bad = subwindow.document.getElementById("test-video-bad"); + video_bad2 = subwindow.document.getElementById("test-video-bad2"); + iframe = subwindow.document.getElementById("test-iframe"); + video_in_iframe = subwindow.document.getElementById("test-video-in-iframe").contentDocument.getElementsByTagName("video")[0]; + // Ensure that contextmenu has 'context-media-play' item when check runs. + video_in_iframe.pause(); + image_in_iframe = subwindow.document.getElementById("test-image-in-iframe").contentDocument.getElementsByTagName("img")[0]; + textarea = subwindow.document.getElementById("test-textarea"); + textarea_sel = subwindow.document.getElementById("test-textarea-sel"); + contenteditable = subwindow.document.getElementById("test-contenteditable"); + contenteditable.focus(); // content editable needs to be focused to enable spellcheck + inputspell = subwindow.document.getElementById("test-input-spellcheck"); + pagemenu = subwindow.document.getElementById("test-pagemenu"); + + // SeaMonkey specific elements. + img_link = subwindow.document.getElementById("test-image-link"); + img_mailto = subwindow.document.getElementById("test-image-mailto"); + + contextMenu.addEventListener("popupshown", function() { runTest(++testNum); }); + runTest(1); +} + +// We open this in a separate window, because the Mochitests run inside a frame. +// The frame causes an extra menu item, and prevents running the test +// standalone (ie, clicking the test name in the Mochitest window) to see +// success/failure messages. +var painted = false, loaded = false; + +function waitForEvents(event) +{ + if (event.type == "MozAfterPaint") + painted = true; + else if (event.type == "load") + loaded = true; + if (painted && loaded) { + subwindow.removeEventListener("MozAfterPaint", waitForEvents); + subwindow.onload = null; + startTest(); + } +} + +var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=700"); +subwindow.addEventListener("MozAfterPaint", waitForEvents); +subwindow.onload = waitForEvents; + +SimpleTest.waitForExplicitFinish(); +</script> +</pre> +</body> +</html> diff --git a/comm/suite/browser/test/mochitest/test_feed_discovery.html b/comm/suite/browser/test/mochitest/test_feed_discovery.html new file mode 100644 index 0000000000..2f2a0a459e --- /dev/null +++ b/comm/suite/browser/test/mochitest/test_feed_discovery.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=240393 +--> +<head> + <title>Test for feed discovery</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=377611">Mozilla Bug 377611</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody"> + +/** Tests for bug 240393 (from bug 377611 on Firefox side) **/ + +var rv = { tests: null }; +var testCheckInterval = null; + +function startTest() { + var url = window.location.href.replace(/test_feed_discovery\.html/, + 'feed_discovery.html'); + SpecialPowers.openDialog(window, [url, '', 'dialog=no,width=10,height=10', rv]); + testCheckInterval = window.setInterval(tryIfTestIsFinished, 500); +} + +function tryIfTestIsFinished() { + if (rv.tests) { + window.clearInterval(testCheckInterval); + checkTest(); + } +} + +function checkTest() { + for (var i = 0; i < rv.tests.length; ++ i) { + var test = rv.tests[i]; + if (test.todo) + todo(test.check, test.message); + else + ok(test.check, test.message); + } + SimpleTest.finish(); +} + +window.onload = startTest; + +SimpleTest.waitForExplicitFinish(); +</script> +</pre> +</body> +</html> diff --git a/comm/suite/browser/test/mochitest/test_registerHandler.html b/comm/suite/browser/test/mochitest/test_registerHandler.html new file mode 100644 index 0000000000..20952ca9ee --- /dev/null +++ b/comm/suite/browser/test/mochitest/test_registerHandler.html @@ -0,0 +1,85 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=402788 +--> +<head> + <title>Test for Bug 402788</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=402788">Mozilla Bug 402788</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody"> + +/** Test for Bug 402788 **/ + + // return false if an exception has been catched, true otherwise + function testRegisterHandler(aIsProtocol, aTxt, aUri, aTitle) + { + try { + if (aIsProtocol) + navigator.registerProtocolHandler(aTxt, aUri, aTitle); + else + navigator.registerContentHandler(aTxt, aUri, aTitle); + } + catch(e) { + return false; + } + + return true; + } + + ok(navigator.registerProtocolHandler, "navigator.registerProtocolHandler should be defined"); + ok(navigator.registerContentHandler, "navigator.registerContentHandler should be defined"); + + // testing a generic case + is(true, testRegisterHandler(true, "foo", "http://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler should work"); + is(true, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler should work"); + + // testing with wrong uris + is(false, testRegisterHandler(true, "foo", "http://mochi.test:8888/", "Foo handler"), "a protocol handler uri should contain %s"); + is(false, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/", "Foo handler"), "a content handler uri should contain %s"); + + // the spec says we should not throw here, but it probably needs to be changed + is(false, testRegisterHandler(true, "foo", "foo/%s", "Foo handler"), "a protocol handler uri should be valid"); + is(false, testRegisterHandler(false, "application/rss+xml", "foo/%s", "Foo handler"), "a content handler uri should be valid"); + + // we should only accept to register when the handler has the same host as the current page (bug 402287) + is(false, testRegisterHandler(true, "foo", "http://remotehost:8888/%s", "Foo handler"), "registering a foo protocol handler with a different host should not work"); + is(false, testRegisterHandler(false, "application/rss+xml", "http://remotehost:8888/%s", "Foo handler"), "registering a foo content handler with a different host should not work"); + + // restriction to http(s) for the uri of the handler (bug 401343) + // https should work (http already tested in the generic case) + is(true, testRegisterHandler(true, "foo", "https://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with https scheme should work"); + is(true, testRegisterHandler(false, "application/rss+xml", "https://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with https scheme should work"); + // ftp should not work + is(false, testRegisterHandler(true, "foo", "ftp://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with ftp scheme should not work"); + is(false, testRegisterHandler(false, "application/rss+xml", "ftp://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with ftp scheme should not work"); + // chrome should not work + is(false, testRegisterHandler(true, "foo", "chrome://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with chrome scheme should not work"); + is(false, testRegisterHandler(false, "application/rss+xml", "chrome://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with chrome scheme should not work"); + // foo should not work + is(false, testRegisterHandler(true, "foo", "foo://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with foo scheme should not work"); + is(false, testRegisterHandler(false, "application/rss+xml", "foo://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with foo scheme should not work"); + + // for security reasons, protocol handlers should never be registered for some schemes (chrome, vbscript, ...) (bug 402788) + is(false, testRegisterHandler(true, "chrome", "http://mochi.test:8888/%s", "chrome handler"), "registering a chrome protocol handler should not work"); + is(false, testRegisterHandler(true, "vbscript", "http://mochi.test:8888/%s", "vbscript handler"), "registering a vbscript protocol handler should not work"); + is(false, testRegisterHandler(true, "javascript", "http://mochi.test:8888/%s", "javascript handler"), "registering a javascript protocol handler should not work"); + is(false, testRegisterHandler(true, "moz-icon", "http://mochi.test:8888/%s", "moz-icon handler"), "registering a moz-icon protocol handler should not work"); + + // for security reasons, content handlers should never be registered for some types (html, ...) + is(true, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/%s", "Foo handler"), "registering rss content handlers should work"); + is(true, testRegisterHandler(false, "application/atom+xml", "http://mochi.test:8888/%s", "Foo handler"), "registering atom content handlers should work"); + todo(false, testRegisterHandler(false, "text/html", "http://mochi.test:8888/%s", "Foo handler"), "registering html content handlers should not work"); // bug 403798 + +</script> +</pre> +</body> +</html> diff --git a/comm/suite/browser/test/mochitest/valid-feed.xml b/comm/suite/browser/test/mochitest/valid-feed.xml new file mode 100644 index 0000000000..0e700b6d8d --- /dev/null +++ b/comm/suite/browser/test/mochitest/valid-feed.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<feed xmlns="http://www.w3.org/2005/Atom"> + + <title>Example Feed</title> + <link href="http://example.org/"/> + <updated>2010-08-22T18:30:02Z</updated> + + <author> + <name>John Doe</name> + </author> + <id>urn:uuid:e2df8375-99be-4848-b05e-b9d407555267</id> + + <entry> + + <title>Item</title> + <link href="http://example.org/first"/> + <id>urn:uuid:9e0f4bed-33d3-4a9d-97ab-ecaa31b3f14a</id> + <updated>2010-08-22T18:30:02Z</updated> + + <summary>Some text.</summary> + </entry> + +</feed> diff --git a/comm/suite/browser/test/mochitest/valid-unsniffable-feed.xml b/comm/suite/browser/test/mochitest/valid-unsniffable-feed.xml new file mode 100644 index 0000000000..e753157395 --- /dev/null +++ b/comm/suite/browser/test/mochitest/valid-unsniffable-feed.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 512 bytes! +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + --> +<feed xmlns="http://www.w3.org/2005/Atom"> + + <title>Example Feed</title> + <link href="http://example.org/"/> + <updated>2010-08-22T18:30:02Z</updated> + + <author> + <name>John Doe</name> + </author> + <id>urn:uuid:e2df8375-99be-4848-b05e-b9d407555267</id> + + <entry> + + <title>Item</title> + <link href="http://example.org/first"/> + <id>urn:uuid:9e0f4bed-33d3-4a9d-97ab-ecaa31b3f14a</id> + <updated>2010-08-22T18:30:02Z</updated> + + <summary>Some text.</summary> + </entry> + +</feed> diff --git a/comm/suite/browser/test/mochitest/video.ogg b/comm/suite/browser/test/mochitest/video.ogg Binary files differnew file mode 100644 index 0000000000..ac7ece3519 --- /dev/null +++ b/comm/suite/browser/test/mochitest/video.ogg |