summaryrefslogtreecommitdiffstats
path: root/toolkit/components/viewsource/test/browser
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/viewsource/test/browser')
-rw-r--r--toolkit/components/viewsource/test/browser/browser.toml30
-rw-r--r--toolkit/components/viewsource/test/browser/browser_bug464222.js17
-rw-r--r--toolkit/components/viewsource/test/browser/browser_bug713810.js31
-rw-r--r--toolkit/components/viewsource/test/browser/browser_contextmenu.js115
-rw-r--r--toolkit/components/viewsource/test/browser/browser_gotoline.js37
-rw-r--r--toolkit/components/viewsource/test/browser/browser_open_docgroup.js41
-rw-r--r--toolkit/components/viewsource/test/browser/browser_partialsource.js46
-rw-r--r--toolkit/components/viewsource/test/browser/browser_srcdoc.js28
-rw-r--r--toolkit/components/viewsource/test/browser/browser_validatefilename.js68
-rw-r--r--toolkit/components/viewsource/test/browser/browser_viewsource_newwindow.js102
-rw-r--r--toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js203
-rw-r--r--toolkit/components/viewsource/test/browser/file_bug464222.html1
-rw-r--r--toolkit/components/viewsource/test/browser/head.js231
13 files changed, 950 insertions, 0 deletions
diff --git a/toolkit/components/viewsource/test/browser/browser.toml b/toolkit/components/viewsource/test/browser/browser.toml
new file mode 100644
index 0000000000..73e3b0b9e0
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser.toml
@@ -0,0 +1,30 @@
+[DEFAULT]
+support-files = [
+ "head.js",
+ "file_bug464222.html",
+]
+
+["browser_bug464222.js"]
+https_first_disabled = true
+
+["browser_bug713810.js"]
+
+["browser_contextmenu.js"]
+skip-if = ["os == 'mac' && !debug"] # Bug 1713913 - new Fission platform triage
+
+["browser_gotoline.js"]
+
+["browser_open_docgroup.js"]
+
+["browser_partialsource.js"]
+skip-if = ["os == 'mac' && !debug"] # Bug 1713913 - new Fission platform triage
+
+["browser_srcdoc.js"]
+
+["browser_validatefilename.js"]
+
+["browser_viewsource_newwindow.js"]
+https_first_disabled = true
+
+["browser_viewsourceprefs.js"]
+skip-if = ["socketprocess_networking && os == 'linux' && !debug"]
diff --git a/toolkit/components/viewsource/test/browser/browser_bug464222.js b/toolkit/components/viewsource/test/browser/browser_bug464222.js
new file mode 100644
index 0000000000..b14f35e61a
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_bug464222.js
@@ -0,0 +1,17 @@
+const source =
+ "http://example.com/browser/toolkit/components/viewsource/test/browser/file_bug464222.html";
+
+add_task(async function () {
+ let viewSourceTab = await openDocumentSelect(source, "a");
+
+ let href = await SpecialPowers.spawn(
+ viewSourceTab.linkedBrowser,
+ [],
+ async function () {
+ return content.document.querySelectorAll("a[href]")[0].href;
+ }
+ );
+
+ is(href, "view-source:" + source, "Relative links broken?");
+ gBrowser.removeTab(viewSourceTab);
+});
diff --git a/toolkit/components/viewsource/test/browser/browser_bug713810.js b/toolkit/components/viewsource/test/browser/browser_bug713810.js
new file mode 100644
index 0000000000..b798f6cd10
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_bug713810.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const source =
+ '<html xmlns="http://www.w3.org/1999/xhtml"><body><p>This is a paragraph.</p></body></html>';
+
+add_task(async function () {
+ let viewSourceTab = await openDocumentSelect("data:text/html," + source, "p");
+ await SpecialPowers.spawn(viewSourceTab.linkedBrowser, [], async function () {
+ Assert.equal(
+ content.document.body.textContent,
+ "<p>This is a paragraph.</p>",
+ "Correct source for text/html"
+ );
+ });
+ gBrowser.removeTab(viewSourceTab);
+
+ viewSourceTab = await openDocumentSelect(
+ "data:application/xhtml+xml," + source,
+ "p"
+ );
+ await SpecialPowers.spawn(viewSourceTab.linkedBrowser, [], async function () {
+ Assert.equal(
+ content.document.body.textContent,
+ '<p xmlns="http://www.w3.org/1999/xhtml">This is a paragraph.</p>',
+ "Correct source for application/xhtml+xml"
+ );
+ });
+ gBrowser.removeTab(viewSourceTab);
+});
diff --git a/toolkit/components/viewsource/test/browser/browser_contextmenu.js b/toolkit/components/viewsource/test/browser/browser_contextmenu.js
new file mode 100644
index 0000000000..afc8ab8c84
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_contextmenu.js
@@ -0,0 +1,115 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var source =
+ "data:text/html,text<link%20href='http://example.com/'%20/>more%20text<a%20href='mailto:abc@def.ghi'>email</a>";
+var gViewSourceWindow, gContextMenu, gCopyLinkMenuItem, gCopyEmailMenuItem;
+
+var expectedData = [];
+
+add_task(async function () {
+ // Full source in view source tab
+ let newTab = await openDocument(source);
+ await onViewSourceWindowOpen(window);
+
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+
+ for (let test of expectedData) {
+ await checkMenuItems(contextMenu, test[0], test[1], test[2], test[3]);
+ }
+
+ gBrowser.removeTab(newTab);
+
+ // Selection source in view source tab
+ expectedData = [];
+ newTab = await openDocumentSelect(source, "body");
+ await onViewSourceWindowOpen(window);
+
+ contextMenu = document.getElementById("contentAreaContextMenu");
+
+ for (let test of expectedData) {
+ await checkMenuItems(contextMenu, test[0], test[1], test[2], test[3]);
+ }
+
+ gBrowser.removeTab(newTab);
+});
+
+async function onViewSourceWindowOpen(aWindow) {
+ gViewSourceWindow = aWindow;
+
+ gCopyLinkMenuItem = aWindow.document.getElementById("context-copylink");
+ gCopyEmailMenuItem = aWindow.document.getElementById("context-copyemail");
+
+ let browser = gBrowser.selectedBrowser;
+ await SpecialPowers.spawn(browser, [], async function (arg) {
+ let tags = content.document.querySelectorAll("a[href]");
+ Assert.equal(
+ tags[0].href,
+ "view-source:http://example.com/",
+ "Link has correct href"
+ );
+ Assert.equal(tags[1].href, "mailto:abc@def.ghi", "Link has correct href");
+ });
+
+ expectedData.push(["a[href]", true, false, "http://example.com/"]);
+ expectedData.push(["a[href^=mailto]", false, true, "abc@def.ghi"]);
+ expectedData.push(["span", false, false, null]);
+}
+
+async function checkMenuItems(
+ contextMenu,
+ selector,
+ copyLinkExpected,
+ copyEmailExpected,
+ expectedClipboardContent
+) {
+ let browser = gBrowser.selectedBrowser;
+ await SpecialPowers.spawn(browser, [{ selector }], async function (arg) {
+ content.document.querySelector(arg.selector).scrollIntoView();
+ });
+
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ selector,
+ { type: "contextmenu", button: 2 },
+ browser
+ );
+ await popupShownPromise;
+
+ is(
+ gCopyLinkMenuItem.hidden,
+ !copyLinkExpected,
+ "Copy link menuitem is " + (copyLinkExpected ? "not hidden" : "hidden")
+ );
+ is(
+ gCopyEmailMenuItem.hidden,
+ !copyEmailExpected,
+ "Copy email menuitem is " + (copyEmailExpected ? "not hidden" : "hidden")
+ );
+
+ if (copyLinkExpected || copyEmailExpected) {
+ await new Promise((resolve, reject) => {
+ waitForClipboard(
+ expectedClipboardContent,
+ function () {
+ contextMenu.activateItem(
+ copyLinkExpected ? gCopyLinkMenuItem : gCopyEmailMenuItem
+ );
+ },
+ resolve,
+ reject
+ );
+ });
+ } else {
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popuphidden"
+ );
+ contextMenu.hidePopup();
+ await popupHiddenPromise;
+ }
+}
diff --git a/toolkit/components/viewsource/test/browser/browser_gotoline.js b/toolkit/components/viewsource/test/browser/browser_gotoline.js
new file mode 100644
index 0000000000..000b2c6876
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_gotoline.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var content = "line 1\nline 2\nline 3";
+
+add_task(async function () {
+ // First test with text with the text/html mimetype.
+ let tab = await openDocument("data:text/html," + encodeURIComponent(content));
+ await checkViewSource(tab);
+ gBrowser.removeTab(tab);
+
+ tab = await openDocument("data:text/plain," + encodeURIComponent(content));
+ await checkViewSource(tab);
+ gBrowser.removeTab(tab);
+});
+
+var checkViewSource = async function (aTab) {
+ let browser = aTab.linkedBrowser;
+ await SpecialPowers.spawn(browser, [content], async function (text) {
+ is(content.document.body.textContent, text, "Correct content loaded");
+ });
+
+ for (let i = 1; i <= 3; i++) {
+ browser.sendMessageToActor(
+ "ViewSource:GoToLine",
+ {
+ lineNumber: i,
+ },
+ "ViewSourcePage"
+ );
+ await SpecialPowers.spawn(browser, [i], async function (i) {
+ let selection = content.getSelection();
+ Assert.equal(selection.toString(), "line " + i, "Correct text selected");
+ });
+ }
+};
diff --git a/toolkit/components/viewsource/test/browser/browser_open_docgroup.js b/toolkit/components/viewsource/test/browser/browser_open_docgroup.js
new file mode 100644
index 0000000000..071635b998
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_open_docgroup.js
@@ -0,0 +1,41 @@
+"use strict";
+
+/**
+ * Very basic smoketests for the View Source feature, which also
+ * forces on the DocGroup mismatch check that was added in
+ * bug 1340719.
+ */
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.throw_on_docgroup_mismatch.enabled", true]],
+ });
+});
+
+/**
+ * Tests that we can open View Source in a tab.
+ */
+add_task(async function test_view_source_in_tab() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "http://example.com",
+ },
+ async function (browser) {
+ let sourceTab = await openViewSourceForBrowser(browser);
+ let sourceBrowser = sourceTab.linkedBrowser;
+
+ await SpecialPowers.spawn(sourceBrowser, [], async function () {
+ Assert.equal(
+ content.document.body.id,
+ "viewsource",
+ "View source mode enabled"
+ );
+ });
+
+ BrowserTestUtils.removeTab(sourceTab);
+ }
+ );
+
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/toolkit/components/viewsource/test/browser/browser_partialsource.js b/toolkit/components/viewsource/test/browser/browser_partialsource.js
new file mode 100644
index 0000000000..d57a265b08
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_partialsource.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const frameSource =
+ "<a href='about:mozilla'>some text</a><a id='other' href='about:about'>other text</a>";
+const sources = [
+ `<html><iframe id="f" srcdoc="${frameSource}"></iframe></html>`,
+ `<html><iframe id="f" src="https://example.com/document-builder.sjs?html=${frameSource}"></iframe></html>`,
+];
+
+add_task(async function partial_source() {
+ for (let source of sources) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html," + source
+ );
+
+ let frameBC = gBrowser.selectedBrowser.browsingContext.children[0];
+
+ await SpecialPowers.spawn(frameBC, [], () => {
+ let element = content.document.getElementById("other");
+ content.focus();
+ content.getSelection().selectAllChildren(element);
+ });
+
+ let sourceTab = await openViewPartialSource("#other", frameBC);
+
+ let browser = gBrowser.selectedBrowser;
+ let textContent = await SpecialPowers.spawn(browser, [], async function () {
+ return content.document.body.textContent;
+ });
+ is(
+ textContent,
+ '<a id="other" href="about:about">other text</a>',
+ "Correct content loaded"
+ );
+ let selection = await SpecialPowers.spawn(browser, [], async function () {
+ return String(content.getSelection());
+ });
+ is(selection, "other text", "Correct text selected");
+
+ gBrowser.removeTab(sourceTab);
+ gBrowser.removeTab(tab);
+ }
+});
diff --git a/toolkit/components/viewsource/test/browser/browser_srcdoc.js b/toolkit/components/viewsource/test/browser/browser_srcdoc.js
new file mode 100644
index 0000000000..fbb07dd0ad
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_srcdoc.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const frameSource = `<a href="about:mozilla">good</a>`;
+const source = `<html><iframe srcdoc='${frameSource}' id="f"></iframe></html>`;
+
+add_task(async function () {
+ let url = `data:text/html,${source}`;
+ await BrowserTestUtils.withNewTab({ gBrowser, url }, checkFrameSource);
+});
+
+async function checkFrameSource() {
+ let sourceTab = await openViewFrameSourceTab("#f");
+ registerCleanupFunction(function () {
+ gBrowser.removeTab(sourceTab);
+ });
+
+ let browser = gBrowser.selectedBrowser;
+ let textContent = await SpecialPowers.spawn(browser, [], async function () {
+ return content.document.body.textContent;
+ });
+ is(textContent, frameSource, "Correct content loaded");
+ let id = await SpecialPowers.spawn(browser, [], async function () {
+ return content.document.body.id;
+ });
+ is(id, "viewsource", "View source mode enabled");
+}
diff --git a/toolkit/components/viewsource/test/browser/browser_validatefilename.js b/toolkit/components/viewsource/test/browser/browser_validatefilename.js
new file mode 100644
index 0000000000..57699df733
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_validatefilename.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function () {
+ let tests = [
+ {
+ uri: "data:text/html,Test",
+ basename: "Untitled",
+ },
+ {
+ uri: "data:text/html,<title>Hello There</title>Test",
+ basename: "Hello There",
+ },
+ ];
+
+ for (let test of tests) {
+ await BrowserTestUtils.withNewTab(test.uri, async browser => {
+ let doc = {
+ characterSet: browser.characterSet,
+ contentType: browser.documentContentType,
+ title: browser.contentTitle,
+ };
+
+ let fl = gViewSourceUtils.getTemporaryFile(
+ browser.currentURI,
+ doc,
+ "text/html"
+ );
+ // Some versions of Windows will crop the extension to three characters so allow both forms.
+ ok(
+ fl.leafName == test.basename + ".htm" ||
+ fl.leafName == test.basename + ".html",
+ "filename title for " + test.basename + " html"
+ );
+
+ doc.contentType = "application/xhtml+xml";
+ fl = gViewSourceUtils.getTemporaryFile(
+ browser.currentURI,
+ doc,
+ "application/xhtml+xml"
+ );
+ ok(
+ fl.leafName == test.basename + ".xht" ||
+ fl.leafName == test.basename + ".xhtml",
+ "filename title for " + test.basename + " xhtml"
+ );
+ });
+ }
+
+ let fl = gViewSourceUtils.getTemporaryFile(
+ Services.io.newURI("http://www.example.com/simple"),
+ null,
+ "text/html"
+ );
+ ok(
+ fl.leafName == "simple.htm" || fl.leafName == "simple.html",
+ "filename title for simple"
+ );
+
+ fl = gViewSourceUtils.getTemporaryFile(
+ Services.io.newURI("http://www.example.com/samplefile.txt"),
+ null,
+ "text/html"
+ );
+ is(fl.leafName, "samplefile.txt", "filename title for samplefile");
+});
diff --git a/toolkit/components/viewsource/test/browser/browser_viewsource_newwindow.js b/toolkit/components/viewsource/test/browser/browser_viewsource_newwindow.js
new file mode 100644
index 0000000000..8a38d94719
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_viewsource_newwindow.js
@@ -0,0 +1,102 @@
+/**
+ * Waits for a View Source window to be opened at a particular
+ * URL.
+ *
+ * @param {string} expectedURL The view-source: URL that's expected.
+ * @resolves {DOM Window} The window that was opened.
+ * @returns {Promise}
+ */
+async function waitForNewViewSourceWindow(expectedURL) {
+ let win = await BrowserTestUtils.domWindowOpened();
+ await BrowserTestUtils.waitForEvent(win, "EndSwapDocShells", true);
+ let browser = win.gBrowser.selectedBrowser;
+ if (browser.currentURI.spec != expectedURL) {
+ await BrowserTestUtils.browserLoaded(browser, false, expectedURL);
+ }
+ return win;
+}
+
+/**
+ * When view_source.tab is set to false, view source should
+ * open in new browser window instead of new tab.
+ */
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["view_source.tab", false]],
+ });
+
+ const PAGE = "http://example.com/";
+ await BrowserTestUtils.withNewTab(
+ {
+ url: PAGE,
+ gBrowser,
+ },
+ async browser => {
+ let winPromise = waitForNewViewSourceWindow("view-source:" + PAGE);
+ BrowserViewSource(browser);
+ let win = await winPromise;
+
+ ok(win, "View Source opened up in a new window.");
+ await BrowserTestUtils.closeWindow(win);
+ }
+ );
+});
+
+/**
+ * When view_source.tab is set to false, view partial source
+ * should open up in new browser window instead of new tab.
+ */
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["view_source.tab", false]],
+ });
+
+ const para = "<p>test</p>";
+ const source = `<html><body>${para}</body></html>`;
+ await BrowserTestUtils.withNewTab(
+ {
+ url: "data:text/html," + source,
+ gBrowser,
+ },
+ async browser => {
+ let winPromise = waitForNewViewSourceWindow(
+ "view-source:data:text/html;charset=utf-8,%3Cp%3E%EF%B7%90test%EF%B7%AF%3C%2Fp%3E"
+ );
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ async function (arg) {
+ let element = content.document.querySelector("p");
+ content.getSelection().selectAllChildren(element);
+ }
+ );
+
+ let contentAreaContextMenuPopup = document.getElementById(
+ "contentAreaContextMenu"
+ );
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ contentAreaContextMenuPopup,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "p",
+ { type: "contextmenu", button: 2 },
+ gBrowser.selectedBrowser
+ );
+ await popupShownPromise;
+
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ contentAreaContextMenuPopup,
+ "popuphidden"
+ );
+ let item = document.getElementById("context-viewpartialsource-selection");
+ contentAreaContextMenuPopup.activateItem(item);
+ await popupHiddenPromise;
+ dump("Before winPromise");
+ let win = await winPromise;
+ dump("After winPromise");
+ ok(win, "View Partial Source opened up in a new window.");
+ await BrowserTestUtils.closeWindow(win);
+ }
+ );
+});
diff --git a/toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js b/toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js
new file mode 100644
index 0000000000..e155a9c87e
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js
@@ -0,0 +1,203 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var plaintextURL = "data:text/plain,hello+world";
+var htmlURL = "about:mozilla";
+
+add_setup(async function () {
+ registerCleanupFunction(function () {
+ SpecialPowers.clearUserPref("view_source.tab_size");
+ SpecialPowers.clearUserPref("view_source.wrap_long_lines");
+ SpecialPowers.clearUserPref("view_source.syntax_highlight");
+ });
+});
+
+add_task(async function () {
+ await exercisePrefs(plaintextURL, false);
+ await exercisePrefs(htmlURL, true);
+});
+
+const contextMenu = document.getElementById("contentAreaContextMenu");
+async function openContextMenu(browser) {
+ info("Opening context menu");
+ const popupShownPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "html",
+ { type: "contextmenu", button: 2 },
+ browser
+ );
+ await popupShownPromise;
+ info("Opened context menu");
+}
+
+async function closeContextMenu() {
+ const popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popuphidden"
+ );
+ contextMenu.hidePopup();
+ await popupHiddenPromise;
+}
+
+async function simulateClick(id) {
+ const popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popuphidden"
+ );
+ contextMenu.activateItem(document.getElementById(id));
+ await popupHiddenPromise;
+}
+
+function getAttribute(id, attribute) {
+ let item = document.getElementById(id);
+ return item.getAttribute(attribute);
+}
+
+var exercisePrefs = async function (source, highlightable) {
+ let tab = await openDocument(source);
+ let browser = tab.linkedBrowser;
+
+ const wrapMenuItem = "context-viewsource-wrapLongLines";
+ const syntaxMenuItem = "context-viewsource-highlightSyntax";
+
+ // Test the default states of these menu items.
+ await checkStyle(browser, "-moz-tab-size", 4);
+ await openContextMenu(browser);
+ await checkStyle(browser, "white-space", "pre");
+ await checkHighlight(browser, highlightable);
+ is(
+ getAttribute(wrapMenuItem, "checked"),
+ "false",
+ "Wrap menu item not checked by default"
+ );
+ is(
+ getAttribute(syntaxMenuItem, "checked"),
+ "true",
+ "Syntax menu item checked by default"
+ );
+ await closeContextMenu();
+
+ // Next, test that the Wrap Long Lines menu item works.
+ let prefReady = waitForPrefChange("view_source.wrap_long_lines");
+ await openContextMenu(browser);
+ await simulateClick(wrapMenuItem);
+ await openContextMenu(browser);
+ await checkStyle(browser, "white-space", "pre-wrap");
+ is(getAttribute(wrapMenuItem, "checked"), "true", "Wrap menu item checked");
+ await prefReady;
+ is(
+ SpecialPowers.getBoolPref("view_source.wrap_long_lines"),
+ true,
+ "Wrap pref set"
+ );
+ await closeContextMenu();
+
+ prefReady = waitForPrefChange("view_source.wrap_long_lines");
+ await openContextMenu(browser);
+ await simulateClick(wrapMenuItem);
+ await openContextMenu(browser);
+ await checkStyle(browser, "white-space", "pre");
+ is(
+ getAttribute(wrapMenuItem, "checked"),
+ "false",
+ "Wrap menu item unchecked"
+ );
+ await prefReady;
+ is(
+ SpecialPowers.getBoolPref("view_source.wrap_long_lines"),
+ false,
+ "Wrap pref set"
+ );
+ await closeContextMenu();
+
+ // Check that the Syntax Highlighting menu item works.
+ prefReady = waitForPrefChange("view_source.syntax_highlight");
+ await openContextMenu(browser);
+ await simulateClick(syntaxMenuItem);
+ await openContextMenu(browser);
+ await checkHighlight(browser, false);
+ is(
+ getAttribute(syntaxMenuItem, "checked"),
+ "false",
+ "Syntax menu item unchecked"
+ );
+ await prefReady;
+ is(
+ SpecialPowers.getBoolPref("view_source.syntax_highlight"),
+ false,
+ "Syntax highlighting pref set"
+ );
+ await closeContextMenu();
+
+ prefReady = waitForPrefChange("view_source.syntax_highlight");
+ await openContextMenu(browser);
+ await simulateClick(syntaxMenuItem);
+ await openContextMenu(browser);
+ await checkHighlight(browser, highlightable);
+ is(
+ getAttribute(syntaxMenuItem, "checked"),
+ "true",
+ "Syntax menu item checked"
+ );
+ await prefReady;
+ is(
+ SpecialPowers.getBoolPref("view_source.syntax_highlight"),
+ true,
+ "Syntax highlighting pref set"
+ );
+ await closeContextMenu();
+ gBrowser.removeTab(tab);
+
+ // Open a new view-source window to check that the prefs are obeyed.
+ SpecialPowers.setIntPref("view_source.tab_size", 2);
+ SpecialPowers.setBoolPref("view_source.wrap_long_lines", true);
+ SpecialPowers.setBoolPref("view_source.syntax_highlight", false);
+
+ tab = await openDocument(source);
+ browser = tab.linkedBrowser;
+
+ await checkStyle(browser, "-moz-tab-size", 2);
+ await openContextMenu(browser);
+ await checkStyle(browser, "white-space", "pre-wrap");
+ await checkHighlight(browser, false);
+ is(getAttribute(wrapMenuItem, "checked"), "true", "Wrap menu item checked");
+ is(
+ getAttribute(syntaxMenuItem, "checked"),
+ "false",
+ "Syntax menu item unchecked"
+ );
+
+ SpecialPowers.clearUserPref("view_source.tab_size");
+ SpecialPowers.clearUserPref("view_source.wrap_long_lines");
+ SpecialPowers.clearUserPref("view_source.syntax_highlight");
+
+ await closeContextMenu();
+ gBrowser.removeTab(tab);
+};
+
+var checkStyle = async function (browser, styleProperty, expected) {
+ let value = await SpecialPowers.spawn(
+ browser,
+ [styleProperty],
+ async function (styleProperty) {
+ let style = content.getComputedStyle(content.document.body);
+ return style.getPropertyValue(styleProperty);
+ }
+ );
+ is(value, "" + expected, "Correct value of " + styleProperty);
+};
+
+var checkHighlight = async function (browser, expected) {
+ let highlighted = await SpecialPowers.spawn(browser, [], async function () {
+ let spans = content.document.getElementsByTagName("span");
+ return Array.prototype.some.call(spans, span => {
+ let style = content.getComputedStyle(span);
+ return style.getPropertyValue("color") !== "rgb(0, 0, 0)";
+ });
+ });
+ is(highlighted, expected, "Syntax highlighting " + (expected ? "on" : "off"));
+};
diff --git a/toolkit/components/viewsource/test/browser/file_bug464222.html b/toolkit/components/viewsource/test/browser/file_bug464222.html
new file mode 100644
index 0000000000..f3b00949c7
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/file_bug464222.html
@@ -0,0 +1 @@
+<a href="file_bug464222.html">I'm a link</a>
diff --git a/toolkit/components/viewsource/test/browser/head.js b/toolkit/components/viewsource/test/browser/head.js
new file mode 100644
index 0000000000..a75c6a8658
--- /dev/null
+++ b/toolkit/components/viewsource/test/browser/head.js
@@ -0,0 +1,231 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const { Preferences } = ChromeUtils.importESModule(
+ "resource://gre/modules/Preferences.sys.mjs"
+);
+
+/**
+ * Wait for view source tab after calling given function to open it.
+ *
+ * @param open - a function to open view source.
+ * @returns the new tab which shows the source.
+ */
+async function waitForViewSourceTab(open) {
+ let sourceLoadedPromise;
+ let tabPromise;
+
+ tabPromise = new Promise(resolve => {
+ gBrowser.tabContainer.addEventListener(
+ "TabOpen",
+ event => {
+ let tab = event.target;
+ sourceLoadedPromise = waitForSourceLoaded(tab);
+ resolve(tab);
+ },
+ { once: true }
+ );
+ });
+
+ await open();
+
+ let tab = await tabPromise;
+ await sourceLoadedPromise;
+ return tab;
+}
+
+/**
+ * Opens view source for a browser.
+ *
+ * @param browser - the <xul:browser> to open view source for.
+ * @returns the new tab which shows the source.
+ */
+function openViewSourceForBrowser(browser) {
+ return waitForViewSourceTab(() => {
+ window.BrowserViewSource(browser);
+ });
+}
+
+/**
+ * Opens a view source tab. (View Source)
+ * within the currently selected browser in gBrowser.
+ *
+ * @returns the new tab which shows the source.
+ */
+async function openViewSource() {
+ let contentAreaContextMenuPopup = document.getElementById(
+ "contentAreaContextMenu"
+ );
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ contentAreaContextMenuPopup,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "body",
+ { type: "contextmenu", button: 2 },
+ gBrowser.selectedBrowser
+ );
+ await popupShownPromise;
+
+ return waitForViewSourceTab(async () => {
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ contentAreaContextMenuPopup,
+ "popuphidden"
+ );
+ contentAreaContextMenuPopup.activateItem(
+ document.getElementById("context-viewsource")
+ );
+ await popupHiddenPromise;
+ });
+}
+
+/**
+ * Opens a view source tab for a selection (View Selection Source)
+ * within the currently selected browser in gBrowser.
+ *
+ * @param aCSSSelector - used to specify a node within the selection to
+ * view the source of. It is expected that this node is
+ * within an existing selection.
+ * @param aBrowsingContext - browsing context containing a subframe (optional).
+ * @returns the new tab which shows the source.
+ */
+async function openViewPartialSource(
+ aCSSSelector,
+ aBrowsingContext = gBrowser.selectedBrowser
+) {
+ let contentAreaContextMenuPopup = document.getElementById(
+ "contentAreaContextMenu"
+ );
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ contentAreaContextMenuPopup,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ aCSSSelector,
+ { type: "contextmenu", button: 2 },
+ aBrowsingContext
+ );
+ await popupShownPromise;
+
+ return waitForViewSourceTab(async () => {
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ contentAreaContextMenuPopup,
+ "popuphidden"
+ );
+ let item = document.getElementById("context-viewpartialsource-selection");
+ contentAreaContextMenuPopup.activateItem(item);
+ await popupHiddenPromise;
+ });
+}
+
+/**
+ * Opens a view source tab for a frame (View Frame Source) within the
+ * currently selected browser in gBrowser.
+ *
+ * @param aCSSSelector - used to specify the frame to view the source of.
+ * @returns the new tab which shows the source.
+ */
+async function openViewFrameSourceTab(aCSSSelector) {
+ let contentAreaContextMenuPopup = document.getElementById(
+ "contentAreaContextMenu"
+ );
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ contentAreaContextMenuPopup,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ aCSSSelector,
+ { type: "contextmenu", button: 2 },
+ gBrowser.selectedBrowser
+ );
+ await popupShownPromise;
+
+ let frameContextMenu = document.getElementById("frame");
+ popupShownPromise = BrowserTestUtils.waitForEvent(
+ frameContextMenu,
+ "popupshown"
+ );
+ frameContextMenu.openMenu(true);
+ await popupShownPromise;
+
+ return waitForViewSourceTab(async () => {
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ frameContextMenu,
+ "popuphidden"
+ );
+ let item = document.getElementById("context-viewframesource");
+ frameContextMenu.menupopup.activateItem(item);
+ await popupHiddenPromise;
+ });
+}
+
+/**
+ * For a given view source tab, wait for the source loading step to
+ * complete.
+ */
+function waitForSourceLoaded(tab) {
+ return BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "pageshow",
+ false,
+ event => String(event.target.location).startsWith("view-source")
+ );
+}
+
+/**
+ * Open a new document in a new tab, select part of it, and view the source of
+ * that selection. The document is not closed afterwards.
+ *
+ * @param aURI - url to load
+ * @param aCSSSelector - used to specify a node to select. All of this node's
+ * children will be selected.
+ * @returns the new tab which shows the source.
+ */
+async function openDocumentSelect(aURI, aCSSSelector) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, aURI);
+ registerCleanupFunction(function () {
+ gBrowser.removeTab(tab);
+ });
+
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [{ selector: aCSSSelector }],
+ async function (arg) {
+ let element = content.document.querySelector(arg.selector);
+ content.getSelection().selectAllChildren(element);
+ }
+ );
+
+ return openViewPartialSource(aCSSSelector);
+}
+
+/**
+ * Open a new document in a new tab and view the source of whole page.
+ * The document is not closed afterwards.
+ *
+ * @param aURI - url to load
+ * @returns the new tab which shows the source.
+ */
+async function openDocument(aURI) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, aURI);
+ registerCleanupFunction(function () {
+ gBrowser.removeTab(tab);
+ });
+
+ return openViewSource();
+}
+
+function pushPrefs(...aPrefs) {
+ return SpecialPowers.pushPrefEnv({ set: aPrefs });
+}
+
+function waitForPrefChange(pref) {
+ let deferred = Promise.withResolvers();
+ let observer = () => {
+ Preferences.ignore(pref, observer);
+ deferred.resolve();
+ };
+ Preferences.observe(pref, observer);
+ return deferred.promise;
+}