summaryrefslogtreecommitdiffstats
path: root/toolkit/content/tests/browser/browser_findbar.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/tests/browser/browser_findbar.js')
-rw-r--r--toolkit/content/tests/browser/browser_findbar.js523
1 files changed, 523 insertions, 0 deletions
diff --git a/toolkit/content/tests/browser/browser_findbar.js b/toolkit/content/tests/browser/browser_findbar.js
new file mode 100644
index 0000000000..b6f116245e
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_findbar.js
@@ -0,0 +1,523 @@
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+ChromeUtils.import("resource://gre/modules/Timer.jsm", this);
+
+const TEST_PAGE_URI = "data:text/html;charset=utf-8,The letter s.";
+// Using 'javascript' schema to bypass E10SUtils.canLoadURIInRemoteType, because
+// it does not allow 'data:' URI to be loaded in the parent process.
+const E10S_PARENT_TEST_PAGE_URI =
+ getRootDirectory(gTestPath) + "file_empty.html";
+const TEST_PAGE_URI_WITHIFRAME =
+ "https://example.com/browser/toolkit/content/tests/browser/file_findinframe.html";
+
+/**
+ * Makes sure that the findbar hotkeys (' and /) event listeners
+ * are added to the system event group and do not get blocked
+ * by calling stopPropagation on a keypress event on a page.
+ */
+add_task(async function test_hotkey_event_propagation() {
+ info("Ensure hotkeys are not affected by stopPropagation.");
+
+ // Opening new tab
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE_URI
+ );
+ let browser = gBrowser.getBrowserForTab(tab);
+ let findbar = await gBrowser.getFindBar();
+
+ // Pressing these keys open the findbar.
+ const HOTKEYS = ["/", "'"];
+
+ // Checking if findbar appears when any hotkey is pressed.
+ for (let key of HOTKEYS) {
+ is(findbar.hidden, true, "Findbar is hidden now.");
+ gBrowser.selectedTab = tab;
+ await SimpleTest.promiseFocus(gBrowser.selectedBrowser);
+ await BrowserTestUtils.sendChar(key, browser);
+ is(findbar.hidden, false, "Findbar should not be hidden.");
+ await closeFindbarAndWait(findbar);
+ }
+
+ // Stop propagation for all keyboard events.
+ await SpecialPowers.spawn(browser, [], () => {
+ const stopPropagation = e => {
+ e.stopImmediatePropagation();
+ };
+ let window = content.document.defaultView;
+ window.addEventListener("keydown", stopPropagation);
+ window.addEventListener("keypress", stopPropagation);
+ window.addEventListener("keyup", stopPropagation);
+ });
+
+ // Checking if findbar still appears when any hotkey is pressed.
+ for (let key of HOTKEYS) {
+ is(findbar.hidden, true, "Findbar is hidden now.");
+ gBrowser.selectedTab = tab;
+ await SimpleTest.promiseFocus(gBrowser.selectedBrowser);
+ await BrowserTestUtils.sendChar(key, browser);
+ is(findbar.hidden, false, "Findbar should not be hidden.");
+ await closeFindbarAndWait(findbar);
+ }
+
+ gBrowser.removeTab(tab);
+});
+
+add_task(async function test_not_found() {
+ info("Check correct 'Phrase not found' on new tab");
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE_URI
+ );
+
+ // Search for the first word.
+ await promiseFindFinished("--- THIS SHOULD NEVER MATCH ---", false);
+ let findbar = gBrowser.getCachedFindBar();
+ is(
+ findbar._findStatusDesc.textContent,
+ findbar._notFoundStr,
+ "Findbar status text should be 'Phrase not found'"
+ );
+
+ gBrowser.removeTab(tab);
+});
+
+add_task(async function test_found() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE_URI
+ );
+
+ // Search for a string that WILL be found, with 'Highlight All' on
+ await promiseFindFinished("S", true);
+ ok(
+ !gBrowser.getCachedFindBar()._findStatusDesc.textContent,
+ "Findbar status should be empty"
+ );
+
+ gBrowser.removeTab(tab);
+});
+
+// Setting first findbar to case-sensitive mode should not affect
+// new tab find bar.
+add_task(async function test_tabwise_case_sensitive() {
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE_URI
+ );
+ let findbar1 = await gBrowser.getFindBar();
+
+ let tab2 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE_URI
+ );
+ let findbar2 = await gBrowser.getFindBar();
+
+ // Toggle case sensitivity for first findbar
+ findbar1.getElement("find-case-sensitive").click();
+
+ gBrowser.selectedTab = tab1;
+
+ // Not found for first tab.
+ await promiseFindFinished("S", true);
+ is(
+ findbar1._findStatusDesc.textContent,
+ findbar1._notFoundStr,
+ "Findbar status text should be 'Phrase not found'"
+ );
+
+ gBrowser.selectedTab = tab2;
+
+ // But it didn't affect the second findbar.
+ await promiseFindFinished("S", true);
+ ok(!findbar2._findStatusDesc.textContent, "Findbar status should be empty");
+
+ gBrowser.removeTab(tab1);
+ gBrowser.removeTab(tab2);
+});
+
+/**
+ * Navigating from a web page (for example mozilla.org) to an internal page
+ * (like about:addons) might trigger a change of browser's remoteness.
+ * 'Remoteness change' means that rendering page content moves from child
+ * process into the parent process or the other way around.
+ * This test ensures that findbar properly handles such a change.
+ */
+add_task(async function test_reinitialization_at_remoteness_change() {
+ // This test only makes sence in e10s evironment.
+ if (!gMultiProcessBrowser) {
+ info("Skipping this test because of non-e10s environment.");
+ return;
+ }
+
+ info("Ensure findbar re-initialization at remoteness change.");
+
+ // Load a remote page and trigger findbar construction.
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE_URI
+ );
+ let browser = gBrowser.getBrowserForTab(tab);
+ let findbar = await gBrowser.getFindBar();
+
+ // Findbar should operate normally.
+ await promiseFindFinished("z", false);
+ is(
+ findbar._findStatusDesc.textContent,
+ findbar._notFoundStr,
+ "Findbar status text should be 'Phrase not found'"
+ );
+
+ await promiseFindFinished("s", false);
+ ok(!findbar._findStatusDesc.textContent, "Findbar status should be empty");
+
+ // Moving browser into the parent process and reloading sample data.
+ ok(browser.isRemoteBrowser, "Browser should be remote now.");
+ await promiseRemotenessChange(tab, false);
+ let docLoaded = BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ E10S_PARENT_TEST_PAGE_URI
+ );
+ BrowserTestUtils.loadURI(browser, E10S_PARENT_TEST_PAGE_URI);
+ await docLoaded;
+ ok(!browser.isRemoteBrowser, "Browser should not be remote any more.");
+ browser.contentDocument.body.append("The letter s.");
+ browser.contentDocument.body.clientHeight; // Force flush.
+
+ // Findbar should keep operating normally after remoteness change.
+ await promiseFindFinished("z", false);
+ is(
+ findbar._findStatusDesc.textContent,
+ findbar._notFoundStr,
+ "Findbar status text should be 'Phrase not found'"
+ );
+
+ await promiseFindFinished("s", false);
+ ok(!findbar._findStatusDesc.textContent, "Findbar status should be empty");
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+/**
+ * Ensure that the initial typed characters aren't lost immediately after
+ * opening the find bar.
+ */
+add_task(async function e10sLostKeys() {
+ // This test only makes sence in e10s evironment.
+ if (!gMultiProcessBrowser) {
+ info("Skipping this test because of non-e10s environment.");
+ return;
+ }
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE_URI
+ );
+
+ ok(!gFindBarInitialized, "findbar isn't initialized yet");
+
+ await gFindBarPromise;
+ let findBar = gFindBar;
+ let initialValue = findBar._findField.value;
+
+ await EventUtils.synthesizeAndWaitKey(
+ "F",
+ { accelKey: true },
+ window,
+ null,
+ () => {
+ // We can't afford to wait for the promise to resolve, by then the
+ // find bar is visible and focused, so sending characters to the
+ // content browser wouldn't work.
+ isnot(
+ document.activeElement,
+ findBar._findField,
+ "findbar is not yet focused"
+ );
+ EventUtils.synthesizeKey("a");
+ EventUtils.synthesizeKey("b");
+ EventUtils.synthesizeKey("c");
+ is(
+ findBar._findField.value,
+ initialValue,
+ "still has initial find query"
+ );
+ }
+ );
+
+ await BrowserTestUtils.waitForCondition(
+ () => findBar._findField.value.length == 3
+ );
+ is(document.activeElement, findBar._findField, "findbar is now focused");
+ is(findBar._findField.value, "abc", "abc fully entered as find query");
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+/**
+ * This test makes sure that keyboard operations still occur
+ * after the findbar is opened and closed.
+ */
+add_task(async function test_open_and_close_keys() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html,<body style='height: 5000px;'>Hello There</body>"
+ );
+
+ await gFindBarPromise;
+ let findBar = gFindBar;
+
+ is(findBar.hidden, true, "Findbar is hidden now.");
+ let openedPromise = BrowserTestUtils.waitForEvent(findBar, "findbaropen");
+ await EventUtils.synthesizeKey("f", { accelKey: true });
+ await openedPromise;
+
+ is(findBar.hidden, false, "Findbar should not be hidden.");
+
+ let closedPromise = BrowserTestUtils.waitForEvent(findBar, "findbarclose");
+ await EventUtils.synthesizeKey("KEY_Escape");
+ await closedPromise;
+
+ let scrollPromise = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "scroll"
+ );
+ await EventUtils.synthesizeKey("KEY_ArrowDown");
+ await scrollPromise;
+
+ let scrollPosition = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async function() {
+ return content.document.body.scrollTop;
+ }
+ );
+
+ ok(scrollPosition > 0, "Scrolled ok to " + scrollPosition);
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+// This test loads an editable area within an iframe and then
+// performs a search. Focusing the editable area should still
+// allow keyboard events to be received.
+add_task(async function test_hotkey_insubframe() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE_URI_WITHIFRAME
+ );
+
+ await gFindBarPromise;
+ let findBar = gFindBar;
+
+ // Focus the editable area within the frame.
+ let browser = gBrowser.selectedBrowser;
+ let frameBC = browser.browsingContext.children[0];
+ await SpecialPowers.spawn(frameBC, [], async () => {
+ content.document.body.focus();
+ content.document.defaultView.focus();
+ });
+
+ // Start a find and wait for the findbar to open.
+ let findBarOpenPromise = BrowserTestUtils.waitForEvent(
+ gBrowser,
+ "findbaropen"
+ );
+ EventUtils.synthesizeKey("f", { accelKey: true });
+ await findBarOpenPromise;
+
+ // Opening the findbar would have focused the find textbox.
+ // Focus the editable area again.
+ let cursorPos = await SpecialPowers.spawn(frameBC, [], async () => {
+ content.document.body.focus();
+ content.document.defaultView.focus();
+ return content.getSelection().anchorOffset;
+ });
+ is(cursorPos, 0, "initial cursor position");
+
+ // Try moving the caret.
+ await BrowserTestUtils.synthesizeKey("KEY_ArrowRight", {}, frameBC);
+
+ cursorPos = await SpecialPowers.spawn(frameBC, [], async () => {
+ return content.getSelection().anchorOffset;
+ });
+ is(cursorPos, 1, "cursor moved");
+
+ await closeFindbarAndWait(findBar);
+ gBrowser.removeTab(tab);
+});
+
+/**
+ * Reloading a page should use the same match case / whole word
+ * state for the search.
+ */
+add_task(async function test_preservestate_on_reload() {
+ for (let stateChange of ["case-sensitive", "entire-word"]) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html,<p>There is a cat named Theo in the kitchen with another cat named Catherine. The two of them are thirsty."
+ );
+
+ // Start a find and wait for the findbar to open.
+ let findBarOpenPromise = BrowserTestUtils.waitForEvent(
+ gBrowser,
+ "findbaropen"
+ );
+ EventUtils.synthesizeKey("f", { accelKey: true });
+ await findBarOpenPromise;
+
+ let isEntireWord = stateChange == "entire-word";
+
+ let findbar = await gBrowser.getFindBar();
+
+ // Find some text.
+ let promiseMatches = promiseGetMatchCount(findbar);
+ await promiseFindFinished("The", true);
+
+ let matches = await promiseMatches;
+ is(matches.current, 1, "Correct match position " + stateChange);
+ is(matches.total, 7, "Correct number of matches " + stateChange);
+
+ // Turn on the case sensitive or entire word option.
+ findbar.getElement("find-" + stateChange).click();
+
+ promiseMatches = promiseGetMatchCount(findbar);
+ gFindBar.onFindAgainCommand();
+ matches = await promiseMatches;
+ is(
+ matches.current,
+ 2,
+ "Correct match position after state change matches " + stateChange
+ );
+ is(
+ matches.total,
+ isEntireWord ? 2 : 3,
+ "Correct number after state change matches " + stateChange
+ );
+
+ // Reload the page.
+ let loadedPromise = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ true
+ );
+ gBrowser.reload();
+ await loadedPromise;
+
+ // Perform a find again. The state should be preserved.
+ promiseMatches = promiseGetMatchCount(findbar);
+ gFindBar.onFindAgainCommand();
+ matches = await promiseMatches;
+ is(
+ matches.current,
+ 1,
+ "Correct match position after reload and find again " + stateChange
+ );
+ is(
+ matches.total,
+ isEntireWord ? 2 : 3,
+ "Correct number of matches after reload and find again " + stateChange
+ );
+
+ // Turn off the case sensitive or entire word option and find again.
+ findbar.getElement("find-" + stateChange).click();
+
+ promiseMatches = promiseGetMatchCount(findbar);
+ gFindBar.onFindAgainCommand();
+ matches = await promiseMatches;
+
+ is(
+ matches.current,
+ isEntireWord ? 4 : 2,
+ "Correct match position after reload and find again reset " + stateChange
+ );
+ is(
+ matches.total,
+ 7,
+ "Correct number of matches after reload and find again reset " +
+ stateChange
+ );
+
+ findbar.clear();
+ await closeFindbarAndWait(findbar);
+
+ gBrowser.removeTab(tab);
+ }
+});
+
+async function promiseFindFinished(searchText, highlightOn) {
+ let findbar = await gBrowser.getFindBar();
+ findbar.startFind(findbar.FIND_NORMAL);
+ let highlightElement = findbar.getElement("highlight");
+ if (highlightElement.checked != highlightOn) {
+ highlightElement.click();
+ }
+ return new Promise(resolve => {
+ executeSoon(() => {
+ findbar._findField.value = searchText;
+
+ let resultListener;
+ // When highlighting is on the finder sends a second "FOUND" message after
+ // the search wraps. This causes timing problems with e10s. waitMore
+ // forces foundOrTimeout wait for the second "FOUND" message before
+ // resolving the promise.
+ let waitMore = highlightOn;
+ let findTimeout = setTimeout(() => foundOrTimedout(null), 2000);
+ let foundOrTimedout = function(aData) {
+ if (aData !== null && waitMore) {
+ waitMore = false;
+ return;
+ }
+ if (aData === null) {
+ info("Result listener not called, timeout reached.");
+ }
+ clearTimeout(findTimeout);
+ findbar.browser.finder.removeResultListener(resultListener);
+ resolve();
+ };
+
+ resultListener = {
+ onFindResult: foundOrTimedout,
+ onCurrentSelection() {},
+ onMatchesCountResult() {},
+ onHighlightFinished() {},
+ };
+ findbar.browser.finder.addResultListener(resultListener);
+ findbar._find();
+ });
+ });
+}
+
+function promiseGetMatchCount(findbar) {
+ return new Promise(resolve => {
+ let resultListener = {
+ onFindResult() {},
+ onCurrentSelection() {},
+ onHighlightFinished() {},
+ onMatchesCountResult(response) {
+ if (response.total > 0) {
+ findbar.browser.finder.removeResultListener(resultListener);
+ resolve(response);
+ }
+ },
+ };
+ findbar.browser.finder.addResultListener(resultListener);
+ });
+}
+
+function promiseRemotenessChange(tab, shouldBeRemote) {
+ return new Promise(resolve => {
+ let browser = gBrowser.getBrowserForTab(tab);
+ tab.addEventListener(
+ "TabRemotenessChange",
+ function() {
+ resolve();
+ },
+ { once: true }
+ );
+ let remoteType = shouldBeRemote
+ ? E10SUtils.DEFAULT_REMOTE_TYPE
+ : E10SUtils.NOT_REMOTE;
+ gBrowser.updateBrowserRemoteness(browser, { remoteType });
+ });
+}