summaryrefslogtreecommitdiffstats
path: root/browser/components/tests/browser
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /browser/components/tests/browser
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/tests/browser')
-rw-r--r--browser/components/tests/browser/browser.ini9
-rw-r--r--browser/components/tests/browser/browser_bug538331.js228
-rw-r--r--browser/components/tests/browser/browser_contentpermissionprompt.js180
-rw-r--r--browser/components/tests/browser/browser_default_bookmark_toolbar_visibility.js82
-rw-r--r--browser/components/tests/browser/browser_initial_tab_remoteType.js211
-rw-r--r--browser/components/tests/browser/browser_startup_homepage.js122
-rw-r--r--browser/components/tests/browser/whats_new_page/active-update.xml1
-rw-r--r--browser/components/tests/browser/whats_new_page/browser.ini12
-rw-r--r--browser/components/tests/browser/whats_new_page/browser_whats_new_page.js108
-rw-r--r--browser/components/tests/browser/whats_new_page/updates/0/update.status1
10 files changed, 954 insertions, 0 deletions
diff --git a/browser/components/tests/browser/browser.ini b/browser/components/tests/browser/browser.ini
new file mode 100644
index 0000000000..5299b49511
--- /dev/null
+++ b/browser/components/tests/browser/browser.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+
+[browser_bug538331.js]
+skip-if = !updater
+reason = test depends on update channel
+[browser_contentpermissionprompt.js]
+[browser_default_bookmark_toolbar_visibility.js]
+[browser_initial_tab_remoteType.js]
+[browser_startup_homepage.js]
diff --git a/browser/components/tests/browser/browser_bug538331.js b/browser/components/tests/browser/browser_bug538331.js
new file mode 100644
index 0000000000..874b4aecbe
--- /dev/null
+++ b/browser/components/tests/browser/browser_bug538331.js
@@ -0,0 +1,228 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const PREF_MSTONE = "browser.startup.homepage_override.mstone";
+const PREF_OVERRIDE_URL = "startup.homepage_override_url";
+
+const DEFAULT_PREF_URL = "http://pref.example.com/";
+const DEFAULT_UPDATE_URL = "http://example.com/";
+
+const XML_EMPTY =
+ '<?xml version="1.0"?><updates xmlns=' +
+ '"http://www.mozilla.org/2005/app-update"></updates>';
+
+const XML_PREFIX =
+ '<updates xmlns="http://www.mozilla.org/2005/app-update"' +
+ '><update appVersion="1.0" buildID="20080811053724" ' +
+ 'channel="nightly" displayVersion="Version 1.0" ' +
+ 'installDate="1238441400314" isCompleteUpdate="true" ' +
+ 'name="Update Test 1.0" type="minor" detailsURL=' +
+ '"http://example.com/" previousAppVersion="1.0" ' +
+ 'serviceURL="https://example.com/" ' +
+ 'statusText="The Update was successfully installed" ' +
+ 'foregroundDownload="true"';
+
+const XML_SUFFIX =
+ '><patch type="complete" URL="http://example.com/" ' +
+ 'size="775" selected="true" state="succeeded"/>' +
+ "</update></updates>";
+
+// nsBrowserContentHandler.js defaultArgs tests
+const BCH_TESTS = [
+ {
+ description: "no mstone change and no update",
+ noMstoneChange: true,
+ },
+ {
+ description: "mstone changed and no update",
+ prefURL: DEFAULT_PREF_URL,
+ },
+ {
+ description: "no mstone change and update with 'showURL' for actions",
+ actions: "showURL",
+ noMstoneChange: true,
+ },
+ {
+ description: "update without actions",
+ prefURL: DEFAULT_PREF_URL,
+ },
+ {
+ description: "update with 'showURL' for actions",
+ actions: "showURL",
+ prefURL: DEFAULT_PREF_URL,
+ },
+ {
+ description: "update with 'showURL' for actions and openURL",
+ actions: "showURL",
+ openURL: DEFAULT_UPDATE_URL,
+ },
+ {
+ description: "update with 'extra showURL' for actions",
+ actions: "extra showURL",
+ prefURL: DEFAULT_PREF_URL,
+ },
+ {
+ description: "update with 'extra showURL' for actions and openURL",
+ actions: "extra showURL",
+ openURL: DEFAULT_UPDATE_URL,
+ },
+ {
+ description: "update with 'silent' for actions",
+ actions: "silent",
+ },
+ {
+ description: "update with 'silent showURL extra' for actions and openURL",
+ actions: "silent showURL extra",
+ },
+];
+
+add_task(async function test_bug538331() {
+ // Reset the startup page pref since it may have been set by other tests
+ // and we will assume it is (non-test) default.
+ await SpecialPowers.pushPrefEnv({
+ clear: [["browser.startup.page"]],
+ });
+
+ let originalMstone = Services.prefs.getCharPref(PREF_MSTONE);
+
+ // Set the preferences needed for the test: they will be cleared up
+ // after it runs.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_MSTONE, originalMstone],
+ [PREF_OVERRIDE_URL, DEFAULT_PREF_URL],
+ ],
+ });
+
+ registerCleanupFunction(async () => {
+ let activeUpdateFile = getActiveUpdateFile();
+ activeUpdateFile.remove(false);
+ reloadUpdateManagerData(true);
+ });
+
+ // Clear any pre-existing override in defaultArgs that are hanging around.
+ // This will also set the browser.startup.homepage_override.mstone preference
+ // if it isn't already set.
+ Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler).defaultArgs;
+
+ for (let i = 0; i < BCH_TESTS.length; i++) {
+ let testCase = BCH_TESTS[i];
+ ok(
+ true,
+ "Test nsBrowserContentHandler " + (i + 1) + ": " + testCase.description
+ );
+
+ if (testCase.actions) {
+ let actionsXML = ' actions="' + testCase.actions + '"';
+ if (testCase.openURL) {
+ actionsXML += ' openURL="' + testCase.openURL + '"';
+ }
+ writeUpdatesToXMLFile(XML_PREFIX + actionsXML + XML_SUFFIX);
+ } else {
+ writeUpdatesToXMLFile(XML_EMPTY);
+ }
+
+ reloadUpdateManagerData(false);
+
+ let noOverrideArgs = Cc["@mozilla.org/browser/clh;1"].getService(
+ Ci.nsIBrowserHandler
+ ).defaultArgs;
+
+ let overrideArgs = "";
+ if (testCase.prefURL) {
+ overrideArgs = testCase.prefURL;
+ } else if (testCase.openURL) {
+ overrideArgs = testCase.openURL;
+ }
+
+ if (overrideArgs == "" && noOverrideArgs) {
+ overrideArgs = noOverrideArgs;
+ } else if (noOverrideArgs) {
+ overrideArgs += "|" + noOverrideArgs;
+ }
+
+ if (testCase.noMstoneChange === undefined) {
+ Services.prefs.setCharPref(PREF_MSTONE, "PreviousMilestone");
+ }
+
+ let defaultArgs = Cc["@mozilla.org/browser/clh;1"].getService(
+ Ci.nsIBrowserHandler
+ ).defaultArgs;
+ is(defaultArgs, overrideArgs, "correct value returned by defaultArgs");
+
+ if (testCase.noMstoneChange === undefined || !testCase.noMstoneChange) {
+ let newMstone = Services.prefs.getCharPref(PREF_MSTONE);
+ is(
+ originalMstone,
+ newMstone,
+ "preference " + PREF_MSTONE + " should have been updated"
+ );
+ }
+ }
+});
+
+/**
+ * Removes the updates.xml file and returns the nsIFile for the
+ * active-update.xml file.
+ *
+ * @return The nsIFile for the active-update.xml file.
+ */
+function getActiveUpdateFile() {
+ let updateRootDir = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
+ let updatesFile = updateRootDir.clone();
+ updatesFile.append("updates.xml");
+ if (updatesFile.exists()) {
+ // The following is non-fatal.
+ try {
+ updatesFile.remove(false);
+ } catch (e) {}
+ }
+ let activeUpdateFile = updateRootDir.clone();
+ activeUpdateFile.append("active-update.xml");
+ return activeUpdateFile;
+}
+
+/**
+ * Reloads the update xml files.
+ *
+ * @param skipFiles (optional)
+ * If true, the update xml files will not be read and the metadata will
+ * be reset. If false (the default), the update xml files will be read
+ * to populate the update metadata.
+ */
+function reloadUpdateManagerData(skipFiles = false) {
+ Cc["@mozilla.org/updates/update-manager;1"]
+ .getService(Ci.nsIUpdateManager)
+ .QueryInterface(Ci.nsIObserver)
+ .observe(null, "um-reload-update-data", skipFiles ? "skip-files" : "");
+}
+
+/**
+ * Writes the updates specified to the active-update.xml file.
+ *
+ * @param aText
+ * The updates represented as a string to write to the active-update.xml
+ * file.
+ */
+function writeUpdatesToXMLFile(aText) {
+ const PERMS_FILE = 0o644;
+
+ const MODE_WRONLY = 0x02;
+ const MODE_CREATE = 0x08;
+ const MODE_TRUNCATE = 0x20;
+
+ let activeUpdateFile = getActiveUpdateFile();
+ if (!activeUpdateFile.exists()) {
+ activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ }
+ let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ let flags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
+ fos.init(activeUpdateFile, flags, PERMS_FILE, 0);
+ fos.write(aText, aText.length);
+ fos.close();
+}
diff --git a/browser/components/tests/browser/browser_contentpermissionprompt.js b/browser/components/tests/browser/browser_contentpermissionprompt.js
new file mode 100644
index 0000000000..123ac100fe
--- /dev/null
+++ b/browser/components/tests/browser/browser_contentpermissionprompt.js
@@ -0,0 +1,180 @@
+/**
+ * These tests test nsBrowserGlue's nsIContentPermissionPrompt
+ * implementation behaviour with various types of
+ * nsIContentPermissionRequests.
+ */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+ChromeUtils.import("resource://gre/modules/Integration.jsm", this);
+
+XPCOMUtils.defineLazyServiceGetter(
+ this,
+ "ContentPermissionPrompt",
+ "@mozilla.org/content-permission/prompt;1",
+ "nsIContentPermissionPrompt"
+);
+
+/**
+ * This is a partial implementation of nsIContentPermissionType.
+ *
+ * @param {string} type
+ * The string defining what type of permission is being requested.
+ * Example: "geo", "desktop-notification".
+ * @return nsIContentPermissionType implementation.
+ */
+function MockContentPermissionType(type) {
+ this.type = type;
+}
+
+MockContentPermissionType.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIContentPermissionType"]),
+ // We expose the wrappedJSObject so that we can be sure
+ // in some of our tests that we're passing the right
+ // nsIContentPermissionType around.
+ wrappedJSObject: this,
+};
+
+/**
+ * This is a partial implementation of nsIContentPermissionRequest.
+ *
+ * @param {Array<nsIContentPermissionType>} typesArray
+ * The types to assign to this nsIContentPermissionRequest,
+ * in order. You probably want to use MockContentPermissionType.
+ * @return nsIContentPermissionRequest implementation.
+ */
+function MockContentPermissionRequest(typesArray) {
+ this.types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ for (let type of typesArray) {
+ this.types.appendElement(type);
+ }
+}
+
+MockContentPermissionRequest.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIContentPermissionRequest"]),
+ // We expose the wrappedJSObject so that we can be sure
+ // in some of our tests that we're passing the right
+ // nsIContentPermissionRequest around.
+ wrappedJSObject: this,
+ // For some of our tests, we want to make sure that the
+ // request is cancelled, so we add some instrumentation here
+ // to check that cancel() is called.
+ cancel() {
+ this.cancelled = true;
+ },
+ cancelled: false,
+ principal: Services.scriptSecurityManager.getSystemPrincipal(),
+};
+
+/**
+ * Tests that if the nsIContentPermissionRequest has an empty
+ * types array, that NS_ERROR_UNEXPECTED is thrown, and the
+ * request is cancelled.
+ */
+add_task(async function test_empty_types() {
+ let mockRequest = new MockContentPermissionRequest([]);
+ Assert.throws(
+ () => {
+ ContentPermissionPrompt.prompt(mockRequest);
+ },
+ /NS_ERROR_UNEXPECTED/,
+ "Should have thrown NS_ERROR_UNEXPECTED."
+ );
+ Assert.ok(mockRequest.cancelled, "Should have cancelled the request.");
+});
+
+/**
+ * Tests that if the nsIContentPermissionRequest has more than
+ * one type, that NS_ERROR_UNEXPECTED is thrown, and the request
+ * is cancelled.
+ */
+add_task(async function test_multiple_types() {
+ let mockRequest = new MockContentPermissionRequest([
+ new MockContentPermissionType("test1"),
+ new MockContentPermissionType("test2"),
+ ]);
+
+ Assert.throws(() => {
+ ContentPermissionPrompt.prompt(mockRequest);
+ }, /NS_ERROR_UNEXPECTED/);
+ Assert.ok(mockRequest.cancelled, "Should have cancelled the request.");
+});
+
+/**
+ * Tests that if the nsIContentPermissionRequest has a type that
+ * does not implement nsIContentPermissionType that NS_NOINTERFACE
+ * is thrown, and the request is cancelled.
+ */
+add_task(async function test_not_permission_type() {
+ let mockRequest = new MockContentPermissionRequest([
+ { QueryInterface: ChromeUtils.generateQI([]) },
+ ]);
+
+ Assert.throws(() => {
+ ContentPermissionPrompt.prompt(mockRequest);
+ }, /NS_NOINTERFACE/);
+ Assert.ok(mockRequest.cancelled, "Should have cancelled the request.");
+});
+
+/**
+ * Tests that if the nsIContentPermissionRequest is for a type
+ * that is not recognized, that NS_ERROR_FAILURE is thrown and
+ * the request is cancelled.
+ */
+add_task(async function test_unrecognized_type() {
+ let mockRequest = new MockContentPermissionRequest([
+ new MockContentPermissionType("test1"),
+ ]);
+
+ Assert.throws(() => {
+ ContentPermissionPrompt.prompt(mockRequest);
+ }, /NS_ERROR_FAILURE/);
+ Assert.ok(mockRequest.cancelled, "Should have cancelled the request.");
+});
+
+/**
+ * Tests that if we meet the minimal requirements for a
+ * nsIContentPermissionRequest, that it will be passed to
+ * ContentPermissionIntegration's createPermissionPrompt
+ * method.
+ */
+add_task(async function test_working_request() {
+ let mockType = new MockContentPermissionType("test-permission-type");
+ let mockRequest = new MockContentPermissionRequest([mockType]);
+
+ // mockPermissionPrompt is what createPermissionPrompt
+ // will return. Returning some kind of object should be
+ // enough to convince nsBrowserGlue that everything went
+ // okay.
+ let didPrompt = false;
+ let mockPermissionPrompt = {
+ prompt() {
+ didPrompt = true;
+ },
+ };
+
+ let integration = base => ({
+ createPermissionPrompt(type, request) {
+ Assert.equal(type, "test-permission-type");
+ Assert.ok(
+ Object.is(request.wrappedJSObject, mockRequest.wrappedJSObject)
+ );
+ return mockPermissionPrompt;
+ },
+ });
+
+ // Register an integration so that we can capture the
+ // calls into ContentPermissionIntegration.
+ try {
+ Integration.contentPermission.register(integration);
+
+ ContentPermissionPrompt.prompt(mockRequest);
+ Assert.ok(!mockRequest.cancelled, "Should not have cancelled the request.");
+ Assert.ok(didPrompt, "Should have tried to show the prompt");
+ } finally {
+ Integration.contentPermission.unregister(integration);
+ }
+});
diff --git a/browser/components/tests/browser/browser_default_bookmark_toolbar_visibility.js b/browser/components/tests/browser/browser_default_bookmark_toolbar_visibility.js
new file mode 100644
index 0000000000..6dab7ed1ed
--- /dev/null
+++ b/browser/components/tests/browser/browser_default_bookmark_toolbar_visibility.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test PlacesUIUtils.maybeToggleBookmarkToolbarVisibility() code running for new profiles.
+ * Ensure that the bookmarks toolbar is hidden in a default configuration.
+ * If new default bookmarks are added to the toolbar then the threshold of > 3
+ * in NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE may need to be adjusted there.
+ */
+add_task(async function test_default_bookmark_toolbar_visibility() {
+ // The Bookmarks Toolbar visibility state should be set only after
+ // Places has notified that it's done initializing.
+ const browserGlue = Cc["@mozilla.org/browser/browserglue;1"].getService(
+ Ci.nsIObserver
+ );
+
+ let placesInitCompleteObserved = TestUtils.topicObserved(
+ "places-browser-init-complete"
+ );
+
+ // If places-browser-init-complete has already notified, this will cause it
+ // to notify again. Otherwise, we wait until the notify is done.
+ browserGlue.observe(
+ null,
+ "browser-glue-test",
+ "places-browser-init-complete"
+ );
+
+ await placesInitCompleteObserved;
+
+ const BROWSER_DOCURL = AppConstants.BROWSER_CHROME_URL;
+ let xulStore = Services.xulStore;
+
+ is(
+ xulStore.getValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed"),
+ "",
+ "Check that @collapsed isn't persisted"
+ );
+ ok(
+ document.getElementById("PersonalToolbar").collapsed,
+ "The bookmarks toolbar should be collapsed by default"
+ );
+});
+
+/**
+ * Ensure that the bookmarks toolbar is visible in a new profile
+ * if the toolbar has > 3 (NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE) bookmarks.
+ */
+add_task(async function test_bookmark_toolbar_visible_when_populated() {
+ const { Bookmarks } = ChromeUtils.import(
+ "resource://gre/modules/Bookmarks.jsm"
+ );
+ const { PlacesUIUtils } = ChromeUtils.import(
+ "resource:///modules/PlacesUIUtils.jsm"
+ );
+
+ let bookmark = {
+ type: Bookmarks.TYPE_BOOKMARK,
+ parentGuid: Bookmarks.toolbarGuid,
+ };
+ let bookmarksInserted = await Promise.all([
+ Bookmarks.insert(Object.assign({ url: "https://example.com/1" }, bookmark)),
+ Bookmarks.insert(Object.assign({ url: "https://example.com/2" }, bookmark)),
+ Bookmarks.insert(Object.assign({ url: "https://example.com/3" }, bookmark)),
+ Bookmarks.insert(Object.assign({ url: "https://example.com/4" }, bookmark)),
+ Bookmarks.insert(Object.assign({ url: "https://example.com/5" }, bookmark)),
+ Bookmarks.insert(Object.assign({ url: "https://example.com/6" }, bookmark)),
+ ]);
+
+ PlacesUIUtils.maybeToggleBookmarkToolbarVisibility();
+
+ const personalToolbar = document.getElementById("PersonalToolbar");
+ ok(
+ !personalToolbar.collapsed,
+ "The bookmarks toolbar should be visible since it has many bookmarks"
+ );
+
+ for (let insertedBookmark of bookmarksInserted) {
+ await Bookmarks.remove(insertedBookmark.guid);
+ }
+ personalToolbar.collapsed = true;
+});
diff --git a/browser/components/tests/browser/browser_initial_tab_remoteType.js b/browser/components/tests/browser/browser_initial_tab_remoteType.js
new file mode 100644
index 0000000000..a8b479dbbb
--- /dev/null
+++ b/browser/components/tests/browser/browser_initial_tab_remoteType.js
@@ -0,0 +1,211 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * These tests test that the initial browser tab has the right
+ * process type assigned to it on creation, which avoids needless
+ * process flips.
+ */
+
+"use strict";
+
+const PRIVILEGEDABOUT_PROCESS_PREF =
+ "browser.tabs.remote.separatePrivilegedContentProcess";
+const PRIVILEGEDABOUT_PROCESS_ENABLED = Services.prefs.getBoolPref(
+ PRIVILEGEDABOUT_PROCESS_PREF
+);
+
+const REMOTE_BROWSER_SHOWN = "remote-browser-shown";
+
+// When the privileged content process is enabled, we expect about:home
+// to load in it. Otherwise, it's in a normal web content process.
+const EXPECTED_ABOUTHOME_REMOTE_TYPE = PRIVILEGEDABOUT_PROCESS_ENABLED
+ ? E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE
+ : E10SUtils.DEFAULT_REMOTE_TYPE;
+
+/**
+ * Test helper function that takes an nsICommandLine, and passes it
+ * into the default command line handler for the browser. It expects
+ * a new browser window to open, and then checks that the expected page
+ * loads in the initial tab in the expected remote type, without doing
+ * unnecessary process flips. The helper function then closes the window.
+ *
+ * @param aCmdLine (nsICommandLine)
+ * The command line to be processed by the default
+ * nsICommandLineHandler
+ * @param aExpectedURL (string)
+ * The URL that the initial browser tab is expected to load.
+ * @param aRemoteType (string)
+ * The expected remoteType on the initial browser tab.
+ * @returns Promise
+ * Resolves once the checks have completed, and the opened window
+ * have been closed.
+ */
+async function assertOneRemoteBrowserShown(
+ aCmdLine,
+ aExpectedURL,
+ aRemoteType
+) {
+ let shownRemoteBrowsers = 0;
+ let observer = () => {
+ shownRemoteBrowsers++;
+ };
+ Services.obs.addObserver(observer, REMOTE_BROWSER_SHOWN);
+
+ let newWinPromise = BrowserTestUtils.waitForNewWindow({
+ url: aExpectedURL,
+ });
+
+ let cmdLineHandler = Cc["@mozilla.org/browser/final-clh;1"].getService(
+ Ci.nsICommandLineHandler
+ );
+ cmdLineHandler.handle(aCmdLine);
+
+ let newWin = await newWinPromise;
+
+ Services.obs.removeObserver(observer, REMOTE_BROWSER_SHOWN);
+
+ if (aRemoteType == E10SUtils.WEB_REMOTE_TYPE) {
+ Assert.ok(
+ E10SUtils.isWebRemoteType(newWin.gBrowser.selectedBrowser.remoteType)
+ );
+ } else {
+ Assert.equal(newWin.gBrowser.selectedBrowser.remoteType, aRemoteType);
+ }
+
+ Assert.equal(
+ shownRemoteBrowsers,
+ 1,
+ "Should have only shown 1 remote browser"
+ );
+ await BrowserTestUtils.closeWindow(newWin);
+}
+
+/**
+ * Constructs an object that implements an nsICommandLine that should
+ * cause the default nsICommandLineHandler to open aURL as the initial
+ * tab in a new window. The returns nsICommandLine is stateful, and
+ * shouldn't be reused.
+ *
+ * @param aURL (string)
+ * The URL to load in the initial tab of the new window.
+ * @returns nsICommandLine
+ */
+function constructOnePageCmdLine(aURL) {
+ return {
+ _arg: aURL,
+ _argCount: 1,
+
+ get length() {
+ return this._argCount;
+ },
+
+ getArgument(aIndex) {
+ if (aIndex == 0 && this._argCount) {
+ return this._arg;
+ }
+ throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
+ },
+
+ findFlag() {
+ return -1;
+ },
+
+ removeArguments() {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ },
+
+ handleFlag() {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ },
+
+ handleFlagWithParam() {
+ if (this._argCount) {
+ this._argCount = 0;
+ return this._arg;
+ }
+
+ return "";
+ },
+
+ get state() {
+ return 0;
+ },
+
+ STATE_INITIAL_LAUNCH: 0,
+ STATE_REMOTE_AUTO: 1,
+ STATE_REMOTE_EXPLICIT: 2,
+
+ preventDefault: false,
+
+ get workingDirectory() {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ },
+
+ get windowContext() {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ },
+
+ resolveFile() {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ },
+
+ resolveURI() {
+ return Services.io.newURI(this._arg);
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsICommandLine"]),
+ };
+}
+
+add_task(async function setup() {
+ NewTabPagePreloading.removePreloadedBrowser(window);
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.newtab.preload", false],
+ ["browser.startup.homepage", "about:home"],
+ ["browser.startup.page", 1],
+ ],
+ });
+});
+
+/**
+ * This tests the default case, where no arguments are passed.
+ */
+add_task(async function test_default_args_and_homescreen() {
+ let cmdLine = Cu.createCommandLine();
+ await assertOneRemoteBrowserShown(
+ cmdLine,
+ "about:home",
+ EXPECTED_ABOUTHOME_REMOTE_TYPE
+ );
+});
+
+/**
+ * This tests the case where about:home is passed as the lone
+ * argument.
+ */
+add_task(async function test_abouthome_arg() {
+ const URI = "about:home";
+ let cmdLine = constructOnePageCmdLine(URI);
+ await assertOneRemoteBrowserShown(
+ cmdLine,
+ URI,
+ EXPECTED_ABOUTHOME_REMOTE_TYPE
+ );
+});
+
+/**
+ * This tests the case where example.com is passed as the lone
+ * argument.
+ */
+add_task(async function test_examplecom_arg() {
+ const URI = "http://example.com/";
+ let cmdLine = constructOnePageCmdLine(URI);
+ await assertOneRemoteBrowserShown(
+ cmdLine,
+ URI,
+ E10SUtils.DEFAULT_REMOTE_TYPE
+ );
+});
diff --git a/browser/components/tests/browser/browser_startup_homepage.js b/browser/components/tests/browser/browser_startup_homepage.js
new file mode 100644
index 0000000000..c6cb4543d5
--- /dev/null
+++ b/browser/components/tests/browser/browser_startup_homepage.js
@@ -0,0 +1,122 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function checkArgs(message, expect, prefs = {}) {
+ info(`Setting prefs: ${JSON.stringify(prefs)}`);
+ await SpecialPowers.pushPrefEnv({
+ set: Object.entries(prefs).map(keyVal => {
+ if (typeof keyVal[1] == "object") {
+ keyVal[1] = JSON.stringify(keyVal[1]);
+ }
+ return keyVal;
+ }),
+ });
+
+ // Check the defaultArgs for startup behavior
+ Assert.equal(
+ Cc["@mozilla.org/browser/clh;1"]
+ .getService(Ci.nsIBrowserHandler)
+ .wrappedJSObject.getArgs(true),
+ expect,
+ message
+ );
+}
+
+add_task(async function test_once_expire() {
+ const url = "https://www.mozilla.org/";
+ await checkArgs("no expiration", url, {
+ "browser.startup.homepage_override.once": { url },
+ });
+
+ await checkArgs("expired", "about:blank", {
+ "browser.startup.homepage_override.once": { expire: 0, url },
+ });
+
+ await checkArgs("not expired", url, {
+ "browser.startup.homepage_override.once": { expire: Date.now() * 2, url },
+ });
+});
+
+add_task(async function test_once_invalid() {
+ await checkArgs("not json", "about:blank", {
+ "browser.startup.homepage_override.once": "https://not.json",
+ });
+
+ await checkArgs("not string", "about:blank", {
+ "browser.startup.homepage_override.once": { url: 5 },
+ });
+
+ await checkArgs("not https", "about:blank", {
+ "browser.startup.homepage_override.once": {
+ url: "http://www.mozilla.org/",
+ },
+ });
+
+ await checkArgs("not portless", "about:blank", {
+ "browser.startup.homepage_override.once": {
+ url: "https://www.mozilla.org:123/",
+ },
+ });
+
+ await checkArgs("invalid protocol", "about:blank", {
+ "browser.startup.homepage_override.once": {
+ url: "data:text/plain,hello world",
+ },
+ });
+
+ await checkArgs("invalid domain", "about:blank", {
+ "browser.startup.homepage_override.once": {
+ url: "https://wwwmozilla.org/",
+ },
+ });
+
+ await checkArgs(
+ "invalid second domain",
+ "https://valid.firefox.com/|https://mozilla.org/",
+ {
+ "browser.startup.homepage_override.once": {
+ url:
+ "https://valid.firefox.com|https://invalidfirefox.com|https://mozilla.org",
+ },
+ }
+ );
+});
+
+add_task(async function test_once() {
+ await checkArgs("initial test prefs (no homepage)", "about:blank");
+
+ const url = "https://www.mozilla.org/";
+ await checkArgs("override once", url, {
+ "browser.startup.homepage_override.once": { url },
+ });
+
+ await checkArgs("once cleared", "about:blank");
+
+ await checkArgs("formatted", "https://www.mozilla.org/en-US", {
+ "browser.startup.homepage_override.once": {
+ url: "https://www.mozilla.org/%LOCALE%",
+ },
+ });
+
+ await checkArgs("use homepage", "about:home", {
+ "browser.startup.page": 1,
+ });
+
+ await checkArgs("once with homepage", `${url}|about:home`, {
+ "browser.startup.homepage_override.once": { url },
+ });
+
+ await checkArgs("once cleared again", "about:home");
+
+ await checkArgs("prefer major version override", `about:welcome|about:home`, {
+ "browser.startup.homepage_override.mstone": "1.0",
+ "browser.startup.homepage_override.once": { url },
+ "startup.homepage_override_url": "about:welcome",
+ });
+
+ await checkArgs("once after major", `${url}|about:home`);
+
+ await checkArgs("once cleared yet again", "about:home");
+});
diff --git a/browser/components/tests/browser/whats_new_page/active-update.xml b/browser/components/tests/browser/whats_new_page/active-update.xml
new file mode 100644
index 0000000000..6e32eb1be2
--- /dev/null
+++ b/browser/components/tests/browser/whats_new_page/active-update.xml
@@ -0,0 +1 @@
+<?xml version="1.0"?><updates xmlns="http://www.mozilla.org/2005/app-update"><update xmlns="http://www.mozilla.org/2005/app-update" appVersion="99999999.0" buildID="20990101111111" channel="test" detailsURL="https://127.0.0.1/" displayVersion="1.0" installDate="1555716429454" isCompleteUpdate="true" name="What's New Page Test" previousAppVersion="60.0" serviceURL="https://127.0.0.1/update.xml" type="minor" platformVersion="99999999.0" actions="showURL" openURL="https://example.com/|https://example.com/"><patch size="1" type="complete" URL="https://127.0.0.1/complete.mar" selected="true" state="pending"/></update></updates>
diff --git a/browser/components/tests/browser/whats_new_page/browser.ini b/browser/components/tests/browser/whats_new_page/browser.ini
new file mode 100644
index 0000000000..a497578b77
--- /dev/null
+++ b/browser/components/tests/browser/whats_new_page/browser.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+skip-if = verify
+reason = This is a startup test. Verify runs tests multiple times after startup.
+support-files =
+ active-update.xml
+ updates/0/update.status
+prefs =
+ app.update.altUpdateDirPath='<test-root>/browser/components/tests/browser/whats_new_page'
+ app.update.disabledForTesting=false
+ browser.startup.homepage_override.mstone="60.0"
+
+[browser_whats_new_page.js]
diff --git a/browser/components/tests/browser/whats_new_page/browser_whats_new_page.js b/browser/components/tests/browser/whats_new_page/browser_whats_new_page.js
new file mode 100644
index 0000000000..2fdec5e7e1
--- /dev/null
+++ b/browser/components/tests/browser/whats_new_page/browser_whats_new_page.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function whats_new_page() {
+ // The test harness will use the current tab and remove the tab's history.
+ // Since the page that is tested is opened prior to the test harness taking
+ // over the current tab the active-update.xml specifies two pages to open by
+ // having 'https://example.com/|https://example.com/' for the value of openURL
+ // and then uses the first tab for the test.
+ gBrowser.selectedTab = gBrowser.tabs[0];
+ // The test harness also changes the page to about:blank so go back to the
+ // page that was originally opened.
+ gBrowser.goBack();
+ // Wait for the page to go back to the original page.
+ await TestUtils.waitForCondition(
+ () =>
+ gBrowser.selectedBrowser &&
+ gBrowser.selectedBrowser.currentURI &&
+ gBrowser.selectedBrowser.currentURI.spec == "https://example.com/",
+ "Waiting for the expected page to reopen"
+ );
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ "https://example.com/",
+ "The what's new page's url should equal https://example.com/"
+ );
+ gBrowser.removeTab(gBrowser.selectedTab);
+
+ let um = Cc["@mozilla.org/updates/update-manager;1"].getService(
+ Ci.nsIUpdateManager
+ );
+ await TestUtils.waitForCondition(
+ () => !um.readyUpdate,
+ "Waiting for the ready update to be removed"
+ );
+ ok(!um.readyUpdate, "There should not be a ready update");
+ await TestUtils.waitForCondition(
+ () => !!um.getUpdateAt(0),
+ "Waiting for the ready update to be moved to the update history"
+ );
+ ok(!!um.getUpdateAt(0), "There should be an update in the update history");
+
+ // Leave no trace. Since this test modifies its support files put them back in
+ // their original state.
+ let alternatePath = Services.prefs.getCharPref("app.update.altUpdateDirPath");
+ let testRoot = Services.prefs.getCharPref("mochitest.testRoot");
+ let relativePath = alternatePath.substring("<test-root>".length);
+ if (AppConstants.platform == "win") {
+ relativePath = relativePath.replace(/\//g, "\\");
+ }
+ alternatePath = testRoot + relativePath;
+ let updateDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ updateDir.initWithPath(alternatePath);
+
+ let activeUpdateFile = updateDir.clone();
+ activeUpdateFile.append("active-update.xml");
+ await TestUtils.waitForCondition(
+ () => !activeUpdateFile.exists(),
+ "Waiting until the active-update.xml file does not exist"
+ );
+
+ let updatesFile = updateDir.clone();
+ updatesFile.append("updates.xml");
+ await TestUtils.waitForCondition(
+ () => updatesFile.exists(),
+ "Waiting until the updates.xml file exists"
+ );
+
+ let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ let flags =
+ FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE;
+
+ let stateSucceeded = "succeeded\n";
+ let updateStatusFile = updateDir.clone();
+ updateStatusFile.append("updates");
+ updateStatusFile.append("0");
+ updateStatusFile.append("update.status");
+ updateStatusFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+ fos.init(updateStatusFile, flags, FileUtils.PERMS_FILE, 0);
+ fos.write(stateSucceeded, stateSucceeded.length);
+ fos.close();
+
+ let xmlContents =
+ '<?xml version="1.0"?><updates xmlns="http://www.mozilla.org/2005/' +
+ 'app-update"><update xmlns="http://www.mozilla.org/2005/app-update" ' +
+ 'appVersion="99999999.0" buildID="20990101111111" channel="test" ' +
+ 'detailsURL="https://127.0.0.1/" displayVersion="1.0" installDate="' +
+ '1555716429454" isCompleteUpdate="true" name="What\'s New Page Test" ' +
+ 'previousAppVersion="60.0" serviceURL="https://127.0.0.1/update.xml" ' +
+ 'type="minor" platformVersion="99999999.0" actions="showURL" ' +
+ 'openURL="https://example.com/|https://example.com/"><patch size="1" ' +
+ 'type="complete" URL="https://127.0.0.1/complete.mar" ' +
+ 'selected="true" state="pending"/></update></updates>\n';
+ activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+ fos.init(activeUpdateFile, flags, FileUtils.PERMS_FILE, 0);
+ fos.write(xmlContents, xmlContents.length);
+ fos.close();
+
+ updatesFile.remove(false);
+ Cc["@mozilla.org/updates/update-manager;1"]
+ .getService(Ci.nsIUpdateManager)
+ .QueryInterface(Ci.nsIObserver)
+ .observe(null, "um-reload-update-data", "");
+});
diff --git a/browser/components/tests/browser/whats_new_page/updates/0/update.status b/browser/components/tests/browser/whats_new_page/updates/0/update.status
new file mode 100644
index 0000000000..774a5c0df4
--- /dev/null
+++ b/browser/components/tests/browser/whats_new_page/updates/0/update.status
@@ -0,0 +1 @@
+succeeded