--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -665,16 +665,20 @@ pref("plugin.defaultXpi.state", 2);
pref("plugin.state.flash", 2);
pref("plugin.state.java", 1);
#ifdef XP_WIN
pref("browser.preferences.instantApply", false);
#else
pref("browser.preferences.instantApply", true);
#endif
+
+// Toggling Search bar on and off in about:preferences
+pref("browser.preferences.search", false);
+
// Once the Storage Management is completed.
// (The Storage Management-related prefs are browser.storageManager.* )
// The Offline(Appcache) Group section in about:preferences will be hidden.
// And the task to clear appcache will be done by Storage Management.
pref("browser.preferences.offlineGroup.enabled", true);
pref("browser.download.show_plugins_in_list", true);
pref("browser.download.hide_plugins_without_extensions", true);
--- a/browser/base/content/test/static/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js
@@ -99,16 +99,20 @@ let gWhitelist = [{
}, {
file: "pocket.properties",
key: "tos",
type: "double-quote"
}, {
file: "aboutNetworking.dtd",
key: "aboutNetworking.logTutorial",
type: "single-quote"
+ }, {
+ file: "preferences.properties",
+ key: "searchResults.needHelp",
+ type: "double-quote"
}
];
/**
* Check if an error should be ignored due to matching one of the whitelist
* objects defined in gWhitelist.
*
* @param filepath The URI spec of the locale file
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/findInPage.js
@@ -0,0 +1,308 @@
+/* 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/. */
+
+/* import-globals-from preferences.js */
+
+var gSearchResultsPane = {
+ findSelection: null,
+ searchResultsCategory: null,
+ searchInput: null,
+
+ init() {
+ let controller = this.getSelectionController();
+ this.findSelection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
+ this.searchResultsCategory = document.getElementById("category-search-results");
+
+ this.searchInput = document.getElementById("searchInput");
+ this.searchInput.hidden = !Services.prefs.getBoolPref("browser.preferences.search");
+ if (!this.searchInput.hidden) {
+ this.searchInput.addEventListener("command", this);
+ this.searchInput.addEventListener("focus", this);
+ }
+ },
+
+ handleEvent(event) {
+ if (event.type === "command") {
+ this.searchFunction(event);
+ } else if (event.type === "focus") {
+ this.initializeCategories();
+ }
+ },
+
+ /**
+ * Check that the passed string matches the filter arguments.
+ *
+ * @param String str
+ * to search for filter words in.
+ * @param String filter
+ * is a string containing all of the words to filter on.
+ * @returns boolean
+ * true when match in string else false
+ */
+ stringMatchesFilters(str, filter) {
+ if (!filter || !str) {
+ return true;
+ }
+ let searchStr = str.toLowerCase();
+ let filterStrings = filter.toLowerCase().split(/\s+/);
+ return !filterStrings.some(f => searchStr.indexOf(f) == -1);
+ },
+
+ categoriesInitialized: false,
+
+ /**
+ * Will attempt to initialize all uninitialized categories
+ */
+ initializeCategories() {
+ // Initializing all the JS for all the tabs
+ if (!this.categoriesInitialized) {
+ this.categoriesInitialized = true;
+ // Each element of gCategoryInits is a name
+ for (let [/* name */, category] of gCategoryInits) {
+ if (!category.inited) {
+ category.init();
+ }
+ }
+ }
+ },
+
+ /**
+ * Finds and returns text nodes within node and all descendants
+ * Iterates through all the sibilings of the node object and adds the sibilings
+ * to an array if sibiling is a TEXT_NODE else checks the text nodes with in current node
+ * Source - http://stackoverflow.com/questions/10730309/find-all-text-nodes-in-html-page
+ *
+ * @param Node nodeObject
+ * DOM element
+ * @returns array of text nodes
+ */
+ textNodeDescendants(node) {
+ if (!node) {
+ return [];
+ }
+ let all = [];
+ for (node = node.firstChild; node; node = node.nextSibling) {
+ if (node.nodeType === node.TEXT_NODE) {
+ all.push(node);
+ } else {
+ all = all.concat(this.textNodeDescendants(node));
+ }
+ }
+ return all;
+ },
+
+ /**
+ * This function is used to find words contained within the text nodes.
+ * We pass in the textNodes because they contain the text to be highlighted.
+ * We pass in the nodeSizes to tell exactly where highlighting need be done.
+ * When creating the range for highlighting, if the nodes are section is split
+ * by an access key, it is important to have the size of each of the nodes summed.
+ * @param Array textNodes
+ * List of DOM elements
+ * @param Array nodeSizes
+ * Running size of text nodes. This will contain the same number of elements as textNodes.
+ * The first element is the size of first textNode element.
+ * For any nodes after, they will contain the summation of the nodes thus far in the array.
+ * Example:
+ * textNodes = [[This is ], [a], [n example]]
+ * nodeSizes = [[8], [9], [18]]
+ * This is used to determine the offset when highlighting
+ * @param String textSearch
+ * Concatination of textNodes's text content
+ * Example:
+ * textNodes = [[This is ], [a], [n example]]
+ * nodeSizes = "This is an example"
+ * This is used when executing the regular expression
+ * @param String searchPhrase
+ * word or words to search for
+ * @returns boolean
+ * Returns true when atleast one instance of search phrase is found, otherwise false
+ */
+ highlightMatches(textNodes, nodeSizes, textSearch, searchPhrase) {
+ let indices = [];
+ let i = -1;
+ while ((i = textSearch.indexOf(searchPhrase, i + 1)) >= 0) {
+ indices.push(i);
+ }
+
+ // Looping through each spot the searchPhrase is found in the concatenated string
+ for (let startValue of indices) {
+ let endValue = startValue + searchPhrase.length;
+ let startNode = null;
+ let endNode = null;
+ let nodeStartIndex = null;
+
+ // Determining the start and end node to highlight from
+ nodeSizes.forEach(function(lengthNodes, index) {
+ // Determining the start node
+ if (!startNode && lengthNodes >= startValue) {
+ startNode = textNodes[index];
+ nodeStartIndex = index;
+ // Calculating the offset when found query is not in the first node
+ if (index > 0) {
+ startValue -= nodeSizes[index - 1];
+ }
+ }
+ // Determining the end node
+ if (!endNode && lengthNodes >= endValue) {
+ endNode = textNodes[index];
+ // Calculating the offset when endNode is different from startNode
+ // or when endNode is not the first node
+ if (index != nodeStartIndex || index > 0 ) {
+ endValue -= nodeSizes[index - 1];
+ }
+ }
+ });
+ let range = document.createRange();
+ range.setStart(startNode, startValue);
+ range.setEnd(endNode, endValue);
+ this.findSelection.addRange(range);
+ }
+
+ return indices.length > 0;
+ },
+
+ getSelectionController() {
+ // Yuck. See bug 138068.
+ let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+
+ let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsISelectionDisplay)
+ .QueryInterface(Ci.nsISelectionController);
+
+ return controller;
+ },
+
+ get strings() {
+ delete this.strings;
+ return this.strings = document.getElementById("searchResultBundle");
+ },
+
+ /**
+ * Shows or hides content according to search input
+ *
+ * @param String event
+ * to search for filted query in
+ */
+ searchFunction(event) {
+ let query = event.target.value.trim().toLowerCase();
+ this.findSelection.removeAllRanges();
+
+ let srHeader = document.getElementById("header-searchResults");
+
+ if (query) {
+ // Showing the Search Results Tag
+ gotoPref("paneSearchResults");
+
+ this.searchResultsCategory.hidden = false;
+
+ let resultsFound = false;
+
+ // Building the range for highlighted areas
+ let rootPreferences = document.getElementById("mainPrefPane")
+ let rootPreferencesChildren = rootPreferences.children;
+
+ // Showing all the children to bind JS, Access Keys, etc
+ for (let i = 0; i < rootPreferences.childElementCount; i++) {
+ rootPreferencesChildren[i].hidden = false;
+ }
+
+ // Showing or Hiding specific section depending on if words in query are found
+ for (let i = 0; i < rootPreferences.childElementCount; i++) {
+ if (rootPreferencesChildren[i].className != "header" &&
+ rootPreferencesChildren[i].className != "no-results-message" &&
+ this.searchWithinNode(rootPreferencesChildren[i], query)) {
+ rootPreferencesChildren[i].hidden = false;
+ resultsFound = true;
+ } else {
+ rootPreferencesChildren[i].hidden = true;
+ }
+ }
+ // It hides Search Results header so turning it on
+ srHeader.hidden = false;
+
+ if (!resultsFound) {
+ let noResultsEl = document.querySelector(".no-results-message");
+ noResultsEl.hidden = false;
+
+ let strings = this.strings;
+ document.getElementById("sorry-message").textContent =
+ strings.getFormattedString("searchResults.sorryMessage", [query]);
+
+ let brandName = document.getElementById("bundleBrand").getString("brandShortName");
+ document.getElementById("need-help").innerHTML =
+ strings.getFormattedString("searchResults.needHelp", [brandName]);
+
+ document.getElementById("need-help-link").setAttribute("href", getHelpLinkURL("search"));
+ }
+ } else {
+ this.searchResultsCategory.hidden = true;
+ document.getElementById("sorry-message").textContent = "";
+ // Going back to General when cleared
+ gotoPref("paneGeneral");
+ }
+ },
+
+ /**
+ * Finding leaf nodes and checking their content for words to search,
+ * It is a recursive function
+ *
+ * @param Node nodeObject
+ * DOM Element
+ * @param String searchPhrase
+ * @returns boolean
+ * Returns true when found in at least one childNode, false otherwise
+ */
+ searchWithinNode(nodeObject, searchPhrase) {
+ let matchesFound = false;
+ if (nodeObject.childElementCount == 0) {
+ let simpleTextNodes = this.textNodeDescendants(nodeObject);
+
+ for (let node of simpleTextNodes) {
+ let result = this.highlightMatches([node], [node.length], node.textContent.toLowerCase(), searchPhrase);
+ matchesFound = matchesFound || result;
+ }
+
+ // Collecting data from boxObject
+ let nodeSizes = [];
+ let allNodeText = "";
+ let runningSize = 0;
+ let labelResult = false;
+ let valueResult = false;
+ let accessKeyTextNodes = this.textNodeDescendants(nodeObject.boxObject);
+
+ for (let node of accessKeyTextNodes) {
+ runningSize += node.textContent.length;
+ allNodeText += node.textContent;
+ nodeSizes.push(runningSize);
+ }
+
+ // Access key are presented
+ let complexTextNodesResult = this.highlightMatches(accessKeyTextNodes, nodeSizes, allNodeText.toLowerCase(), searchPhrase);
+
+ // Searching some elements, such as xul:button, have a 'label' attribute that contains the user-visible text.
+ if (nodeObject.getAttribute("label")) {
+ labelResult = this.stringMatchesFilters(nodeObject.getAttribute("label"), searchPhrase);
+ }
+
+ // Searching some elements, such as xul:label, store their user-visible text in a "value" attribute.
+ if (nodeObject.getAttribute("value")) {
+ valueResult = this.stringMatchesFilters(nodeObject.getAttribute("value"), searchPhrase);
+ }
+
+ matchesFound = matchesFound || complexTextNodesResult || labelResult || valueResult;
+ }
+
+ for (let i = 0; i < nodeObject.childNodes.length; i++) {
+ // Search only if child node is not hidden
+ if (!nodeObject.childNodes[i].hidden) {
+ let result = this.searchWithinNode(nodeObject.childNodes[i], searchPhrase);
+ matchesFound = matchesFound || result;
+ }
+ }
+ return matchesFound;
+ }
+}
--- a/browser/components/preferences/in-content/jar.mn
+++ b/browser/components/preferences/in-content/jar.mn
@@ -8,8 +8,9 @@ browser.jar:
content/browser/preferences/in-content/subdialogs.js
content/browser/preferences/in-content/main.js
content/browser/preferences/in-content/privacy.js
content/browser/preferences/in-content/containers.js
content/browser/preferences/in-content/advanced.js
content/browser/preferences/in-content/applications.js
content/browser/preferences/in-content/sync.js
+ content/browser/preferences/in-content/findInPage.js
--- a/browser/components/preferences/in-content/preferences.js
+++ b/browser/components/preferences/in-content/preferences.js
@@ -5,16 +5,17 @@
// Import globals from the files imported by the .xul files.
/* import-globals-from subdialogs.js */
/* import-globals-from advanced.js */
/* import-globals-from main.js */
/* import-globals-from containers.js */
/* import-globals-from privacy.js */
/* import-globals-from applications.js */
/* import-globals-from sync.js */
+/* import-globals-from findInPage.js */
/* import-globals-from ../../../base/content/utilityOverlay.js */
"use strict";
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
@@ -54,16 +55,18 @@ function init_all() {
gSubDialog.init();
register_module("paneGeneral", gMainPane);
register_module("panePrivacy", gPrivacyPane);
register_module("paneContainers", gContainersPane);
register_module("paneAdvanced", gAdvancedPane);
register_module("paneApplications", gApplicationsPane);
register_module("paneSync", gSyncPane);
+ register_module("paneSearchResults", gSearchResultsPane);
+ gSearchResultsPane.init();
let categories = document.getElementById("categories");
categories.addEventListener("select", event => gotoPref(event.target.value));
document.documentElement.addEventListener("keydown", function(event) {
if (event.keyCode == KeyEvent.DOM_VK_TAB) {
categories.setAttribute("keyboard-navigation", "true");
}
@@ -130,17 +133,17 @@ function telemetryBucketForCategory(cate
}
function onHashChange() {
gotoPref();
}
function gotoPref(aCategory) {
let categories = document.getElementById("categories");
- const kDefaultCategoryInternalName = categories.firstElementChild.value;
+ const kDefaultCategoryInternalName = "paneGeneral";
let hash = document.location.hash;
let category = aCategory || hash.substr(1) || kDefaultCategoryInternalName;
category = friendlyPrefCategoryNameToInternalName(category);
// Updating the hash (below) or changing the selected category
// will re-enter gotoPref.
if (gLastHash == category)
return;
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -70,16 +70,17 @@
<html:link rel="shortcut icon"
href="chrome://browser/skin/preferences/in-content/favicon.ico"/>
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript"
src="chrome://browser/content/preferences/in-content/preferences.js"/>
+ <script src="chrome://browser/content/preferences/in-content/findInPage.js"/>
<script src="chrome://browser/content/preferences/in-content/subdialogs.js"/>
<stringbundle id="bundleBrand"
src="chrome://branding/locale/brand.properties"/>
<stringbundle id="bundlePreferences"
src="chrome://browser/locale/preferences/preferences.properties"/>
<stringbundleset id="appManagerBundleset">
@@ -87,16 +88,27 @@
src="chrome://browser/locale/preferences/applicationManager.properties"/>
</stringbundleset>
<stack flex="1">
<hbox flex="1">
<!-- category list -->
<richlistbox id="categories">
+ <richlistitem id="category-search-results"
+ class="category"
+ value="paneSearchResults"
+ helpTopic="prefs-main"
+ tooltiptext="&paneSearchResults.title;"
+ align="center"
+ hidden="true">
+ <image class="category-icon"/>
+ <label class="category-name" flex="1">&paneSearchResults.title;</label>
+ </richlistitem>
+
<richlistitem id="category-general"
class="category"
value="paneGeneral"
helpTopic="prefs-main"
tooltiptext="&paneGeneral.title;"
align="center">
<image class="category-icon"/>
<label class="category-name" flex="1">&paneGeneral.title;</label>
@@ -152,26 +164,29 @@
<keyset>
<!-- Disable the findbar because it doesn't work properly.
Remove this keyset once bug 1094240 ("disablefastfind" attribute
broken in e10s mode) is fixed. -->
<key key="&focusSearch1.key;" modifiers="accel" id="focusSearch1" oncommand=";"/>
</keyset>
<vbox class="main-content" flex="1">
+ <hbox pack="end">
+ <textbox type="search" id="searchInput" placeholder="&searchInput.label;" hidden="true"/>
+ </hbox>
<prefpane id="mainPrefPane">
+#include searchResults.xul
#include main.xul
#include privacy.xul
#include containers.xul
#include advanced.xul
#include applications.xul
#include sync.xul
</prefpane>
</vbox>
-
</hbox>
<vbox id="dialogOverlay" align="center" pack="center">
<groupbox id="dialogBox"
orient="vertical"
pack="end"
role="dialog"
aria-labelledby="dialogTitle">
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/searchResults.xul
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<stringbundle id="searchResultBundle" src="chrome://browser/locale/preferences/preferences.properties"/>
+
+<hbox id="header-searchResults"
+ class="header"
+ hidden="true"
+ data-category="paneSearchResults">
+ <label class="header-name" flex="1">&paneSearchResults.title;</label>
+ <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
+</hbox>
+
+<groupbox class="no-results-message" align="start" data-category="paneSearchResults" hidden="true">
+ <label id="sorry-message"></label>
+ <label id="need-help"></label>
+</groupbox>
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -7,16 +7,17 @@ support-files =
[browser_applications_selection.js]
[browser_advanced_siteData.js]
[browser_advanced_update.js]
skip-if = !updater
[browser_basic_rebuild_fonts_test.js]
[browser_bug410900.js]
[browser_bug705422.js]
[browser_bug731866.js]
+[browser_search_within_preferences.js]
[browser_bug795764_cachedisabled.js]
[browser_bug1018066_resetScrollPosition.js]
[browser_bug1020245_openPreferences_to_paneContent.js]
[browser_bug1184989_prevent_scrolling_when_preferences_flipped.js]
support-files =
browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
[browser_change_app_handler.js]
skip-if = os != "win" # This test tests the windows-specific app selection dialog, so can't run on non-Windows
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_search_within_preferences.js
@@ -0,0 +1,172 @@
+/*
+* This file contains tests for the Preferences search bar.
+*/
+
+/**
+ * Tests to see if search bar is being hidden when pref is turned off
+ */
+add_task(function*() {
+ yield SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", false]]});
+ yield openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+ let searchInput = gBrowser.contentDocument.querySelectorAll("#searchInput");
+ is(searchInput.length, 1, "There should only be one element name searchInput querySelectorAll");
+ is_element_hidden(searchInput[0], "Search box should be hidden");
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ yield SpecialPowers.popPrefEnv();
+});
+
+// Enabling Searching functionatily. Will display search bar form this testcase forward.
+add_task(function*() {
+ yield SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", true]]});
+});
+
+/**
+ * Tests to see if search bar is being shown when pref is turned on
+ */
+add_task(function*() {
+ yield openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+ let searchInput = gBrowser.contentDocument.getElementById("searchInput");
+ is_element_visible(searchInput, "Search box should be shown");
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+/**
+ * Test for "Search Result" panel.
+ * After it runs a search, it tests if the "Search Results" panel is the only selected category.
+ * The search is then cleared, it then tests if the "General" panel is the only selected category.
+ */
+add_task(function*() {
+ yield openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+
+ // Performs search
+ let searchInput = gBrowser.contentDocument.getElementById("searchInput");
+ searchInput.doCommand()
+ searchInput.value = "password";
+ searchInput.doCommand()
+
+ let categoriesList = gBrowser.contentDocument.getElementById("categories");
+
+ for (let i = 0; i < categoriesList.childElementCount; i++) {
+ let child = categoriesList.children[i]
+ if (child.id == "category-search-results") {
+ is(child.selected, true, "Search results panel should be selected");
+ } else if (child.id) {
+ is(child.selected, false, "No other panel should be selected");
+ }
+ }
+ // Takes search off
+ searchInput.value = "";
+ searchInput.doCommand()
+
+ // Checks if back to generalPane
+ for (let i = 0; i < categoriesList.childElementCount; i++) {
+ let child = categoriesList.children[i]
+ if (child.id == "category-general") {
+ is(child.selected, true, "General panel should be selected");
+ } else if (child.id) {
+ is(child.selected, false, "No other panel should be selected");
+ }
+ }
+
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+/**
+ * Test for "password" case. When we search "password", it should show the "passwordGroup"
+ */
+add_task(function*() {
+ yield openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+
+ // Performs search
+ let searchInput = gBrowser.contentDocument.getElementById("searchInput");
+ searchInput.doCommand()
+ searchInput.value = "password";
+ searchInput.doCommand()
+
+ let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane");
+
+ for (let i = 0; i < mainPrefTag.childElementCount; i++) {
+ let child = mainPrefTag.children[i]
+ if (child.id == "passwordsGroup" || child.id == "weavePrefsDeck" || child.id == "header-searchResults") {
+ is_element_visible(child, "Should be in search results");
+ } else if (child.id) {
+ is_element_hidden(child, "Should not be in search results");
+ }
+ }
+
+ // Takes search off
+ searchInput.value = "";
+ searchInput.doCommand()
+
+ // Checks if back to generalPane
+ for (let i = 0; i < mainPrefTag.childElementCount; i++) {
+ let child = mainPrefTag.children[i]
+ if (child.id == "startupGroup"
+ || child.id == "defaultEngineGroup"
+ || child.id == "oneClickSearchProvidersGroup"
+ || child.id == "paneGeneral"
+ || child.id == "accessibilityGroup"
+ || child.id == "languagesGroup"
+ || child.id == "fontsGroup"
+ || child.id == "browsingGroup"
+ || child.id == "header-general") {
+ is_element_visible(child, "Should be in general tab");
+ } else if (child.id) {
+ is_element_hidden(child, "Should not be in general tab");
+ }
+ }
+
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+/**
+ * Test for if nothing is found
+ */
+add_task(function*() {
+ yield openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+
+ let noResultsEl = gBrowser.contentDocument.querySelector(".no-results-message");
+
+ is_element_hidden(noResultsEl, "Should not be in search results yet");
+
+ // Performs search
+ let searchInput = gBrowser.contentDocument.getElementById("searchInput");
+ searchInput.doCommand()
+ searchInput.value = "coach";
+ searchInput.doCommand()
+
+ is_element_visible(noResultsEl, "Should be in search results");
+
+ // Takes search off
+ searchInput.value = "";
+ searchInput.doCommand()
+
+ is_element_hidden(noResultsEl, "Should not be in search results");
+
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+/**
+ * Test for if we go back to general tab after search case
+ */
+add_task(function*() {
+ yield openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
+ let generalPane = gBrowser.contentDocument.getElementById("header-general");
+
+ is_element_hidden(generalPane, "Should not be in general");
+
+ // Performs search
+ let searchInput = gBrowser.contentDocument.getElementById("searchInput");
+ searchInput.doCommand()
+ searchInput.value = "password";
+ searchInput.doCommand()
+
+ // Takes search off
+ searchInput.value = "";
+ searchInput.doCommand()
+
+ // Checks if back to normal
+ is_element_visible(generalPane, "Should be in generalPane");
+
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.dtd
@@ -8,18 +8,21 @@
<!-- LOCALIZATION NOTE (prefWindow.titleGNOME): This is not used for in-content preferences -->
<!ENTITY prefWindow.titleGNOME "&brandShortName; Preferences">
<!-- When making changes to prefWindow.styleWin test both Windows Classic and
Luna since widget heights are different based on the OS theme -->
<!ENTITY prefWinMinSize.styleWin2 "width: 42em; min-height: 37.5em;">
<!ENTITY prefWinMinSize.styleMac "width: 47em; min-height: 40em;">
<!ENTITY prefWinMinSize.styleGNOME "width: 45.5em; min-height: 40.5em;">
+<!ENTITY paneSearchResults.title "Search Results">
<!ENTITY paneGeneral.title "General">
<!ENTITY paneDownloadLinks.title "Downloads & Links">
<!ENTITY panePrivacySecurity.title "Privacy & Security">
<!ENTITY paneContainers.title "Container Tabs">
<!ENTITY paneUpdates.title "Updates">
<!-- LOCALIZATION NOTE (paneSync1.title): This should match syncBrand.fxAccount.label in ../syncBrand.dtd -->
<!ENTITY paneSync1.title "Firefox Account">
<!ENTITY helpButton.label "Help">
+
+<!ENTITY searchInput.label "Search">
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -246,8 +246,14 @@ removeContainerAlertTitle=Remove This Co
# LOCALIZATION NOTE (removeContainerMsg): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #S is the number of container tabs
removeContainerMsg=If you remove this Container now, #S container tab will be closed. Are you sure you want to remove this Container?;If you remove this Container now, #S container tabs will be closed. Are you sure you want to remove this Container?
removeContainerOkButton=Remove this Container
removeContainerButton2=Don’t remove this Container
+
+# Search Results Pane
+# LOCALIZATION NOTE %S will be replaced by the word being searched
+searchResults.sorryMessage=Sorry! No results were found for “%S”
+# LOCALIZATION NOTE %S gets replaced with the browser name
+searchResults.needHelp=Need help? Visit <html:a id="need-help-link">%S Support</html:a>
--- a/browser/themes/shared/incontentprefs/icons.svg
+++ b/browser/themes/shared/incontentprefs/icons.svg
@@ -63,16 +63,24 @@
c0.8,0.5,1.7,0.7,2.8,0.5c0.4-0.1,0.7-0.2,1-0.4c-0.1,0.1-0.1,0.3-0.2,0.4c0.1,0.1,0.4-0.1,0.6-0.5c0.1-0.1,0.3-0.3,0.6-0.4
c0,0,0,0.1,0,0.2c0,0.1,0,0.1,0,0.2s0,0.1,0.1,0.1c0.4,0,0.7-0.4,1.1-1.3c0.3-0.6,0.5-1.3,0.6-2.2c0.1,0.2,0.2,0.5,0.2,0.8
c0.2-0.5,0.3-1,0.3-1.3c0-0.3,0-1.3-0.1-2.9c0.3,0.4,0.5,0.7,0.7,1.1c0.1-1.2-0.1-2.2-0.5-3.1c-0.4-0.8-0.9-1.5-1.4-1.9
c0.5,0.1,1,0.3,1.4,0.6l-0.3-0.2c-1.4-1.4-3.1-2.2-5.1-2.4c-2-0.2-3.8,0.3-5.4,1.4c-0.6,0-1.1,0-1.5,0.1C3.6,2.7,3.5,2.6,3.5,2.5
C5.3,0.8,7.4,0,9.9,0s4.6,0.8,6.5,2.4L16.2,2c0.6,0.3,1,0.7,1.4,1.2l-0.1-0.3l0,0.1l0-0.1c0.9,0.7,1.4,1.6,1.7,2.7
c0.2,0.9,0.2,1.6,0.1,2.3c0.1,0.1,0.1,0.1,0.1,0.3l0.3-1.1c0.1,0.3,0.2,0.7,0.2,1C20,8.5,20,8.9,20,9.2c0,0.4-0.1,0.7-0.1,1
c-0.1,0.3-0.1,0.7-0.2,1s-0.2,0.6-0.3,0.8c-0.1,0.2-0.2,0.5-0.3,0.7C19.1,12.8,19.2,13,19.3,13.4L19.3,13.4z"/>
</g>
+ <g id="searchResults-shape">
+ <path d="M8,16.3c1.5,0,3-0.4,4.3-1.3l4.6,4.6c0.3,0.3,0.8,0.4,1.2,0.3s0.8-0.5,0.9-0.9s0-0.9-0.3-1.2l-4.5-4.5
+ c2.4-2.9,2.5-7.2,0.2-10.2S8-0.8,4.6,0.8s-5.2,5.4-4.4,9.1S4.2,16.3,8,16.3z M8,1.9c3.4,0,6.1,2.8,6.1,6.2s-2.7,6.2-6.1,6.2
+ S1.9,11.5,1.9,8C1.9,4.6,4.6,1.9,8,1.9L8,1.9z"/>
+ <path d="M8,12.9c2.6,0,4.7-2.1,4.7-4.8S10.6,3.4,8,3.4c-2.6,0-4.7,2.1-4.7,4.7c0,1.3,0.5,2.5,1.4,3.4
+ C5.6,12.4,6.7,12.9,8,12.9L8,12.9z M7.8,4.5c0.4,0,0.8,0.4,0.8,0.8S8.3,6.1,7.8,6.1C6.8,6.1,6,6.9,6,7.9c0,0.4-0.4,0.8-0.8,0.8
+ S4.5,8.3,4.5,7.9C4.5,6,6,4.5,7.8,4.5L7.8,4.5z"/>
+ </g>
</defs>
<use id="general" xlink:href="#general-shape"/>
<use id="general-native" xlink:href="#general-shape"/>
<use id="search" xlink:href="#search-shape"/>
<use id="search-native" xlink:href="#search-shape"/>
<use id="content" xlink:href="#content-shape"/>
<use id="content-native" xlink:href="#content-shape"/>
<use id="applications" xlink:href="#applications-shape"/>
@@ -80,9 +88,11 @@
<use id="privacy" xlink:href="#privacy-shape"/>
<use id="privacy-native" xlink:href="#privacy-shape"/>
<use id="security" xlink:href="#security-shape"/>
<use id="security-native" xlink:href="#security-shape"/>
<use id="sync" xlink:href="#sync-shape"/>
<use id="sync-native" xlink:href="#sync-shape"/>
<use id="advanced" xlink:href="#advanced-shape"/>
<use id="advanced-native" xlink:href="#advanced-shape"/>
+ <use id="searchResults" xlink:href="#searchResults-shape"/>
+ <use id="searchResults-native" xlink:href="#searchResults-shape"/>
</svg>
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -96,16 +96,20 @@ treecol {
#category-sync > .category-icon {
list-style-image: url("chrome://browser/skin/preferences/in-content/icons.svg#sync");
}
#category-advanced > .category-icon {
list-style-image: url("chrome://browser/skin/preferences/in-content/icons.svg#advanced");
}
+#category-search-results > .category-icon {
+ list-style-image: url("chrome://browser/skin/preferences/in-content/icons.svg#searchResults");
+}
+
@media (max-width: 800px) {
.category-name {
display: none;
}
}
/* header */
.header {