author | manotejmeka <manotejmeka@gmail.com> |
Thu, 13 Apr 2017 02:03:50 -0400 | |
changeset 561776 | 687011a6cca6e261db1a26199198300b97dbe644 |
parent 561775 | b70d1598d69dbf3161433328a71f0cf462b13e1f |
child 561777 | 37ee196ecaf5a964c31738ae066325a3b5892d54 |
child 568940 | c0666887f0172d67e7043c8ed0d7d766baa97e6a |
push id | 53861 |
push user | bmo:manotejmeka@gmail.com |
push date | Thu, 13 Apr 2017 06:43:30 +0000 |
bugs | 1349747 |
milestone | 55.0a1 |
--- a/browser/components/preferences/in-content/findInPage.js +++ b/browser/components/preferences/in-content/findInPage.js @@ -1,49 +1,138 @@ /* 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 */ +/* import-globals-from tries.js */ var gSearchResultsPane = { findSelection: null, searchResultsCategory: null, searchInput: null, - listSearchTooltips: [], + listSearchBubbles: [], + initialSearch: true, + trie: null, + timer: 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"); + + let dataList = document.getElementById("searchSuggestions"); + dataList.hidden = !this.searchInput.hidden; if (!this.searchInput.hidden) { + this.searchInput.addEventListener("focus", this); + this.searchInput.addEventListener("input", this); + this.searchInput.addEventListener("keypress", this); this.searchInput.addEventListener("command", this); - this.searchInput.addEventListener("focus", this); + } else { + this.searchInput.removeEventListener("focus", this); + this.searchInput.removeEventListener("input", this); + this.searchInput.removeEventListener("keypress", this); + this.searchInput.removeEventListener("command", this); + } + this.trie = new Trie(); + }, + + /** + * Adding and removing class names to create a clicable close button in searchInput element + * + * @param Boolean classAction + * Determining is X button is already added or not + * @param String className + * Providing class name to bind appropriate CSS styling + */ + classManipulation(classAction, className) { + let nodeObject = this.searchInput + // Adding the class name and listener + if (classAction && !nodeObject.classList.contains(className)) { + nodeObject.classList.add(className); + nodeObject.addEventListener("mousedown", this); + } else if (!classAction && nodeObject.classList.contains(className)) { + // Removing the class name and listener + nodeObject.removeEventListener("mousedown", this); + nodeObject.classList.remove(className); } }, + // chooseDataListOption: "", + handleEvent(event) { if (event.type === "command") { this.searchFunction(event); } else if (event.type === "focus") { // Will attempt to initialize all uninitialized categories // 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(); } } + // Initial search to load tries tree + this.searchFunction("testing search"); + this.searchInput.value = ""; // Clearning old searches + this.searchFunction(""); } - } else if (event.type === "mouseenter" && event.target.classList.contains("search-tooltip")) { + } else if (event.type === "input") { + let query = event.target.value.trim().toLowerCase(); + // Adding a class to show the close button + this.classManipulation(query, "closeButton"); + let overlayClose = document.getElementById("close-button-overlay"); + overlayClose.hidden = false; + overlayClose.addEventListener("click", this); + + // Stopping new search trigger when previous input change is same as + // current input change due to choosing of Datalist option. + if (this.chooseDataListOption != query) { + let datalistOptions = document.getElementById("searchSuggestions").children; + + let word = ""; + for (let optionObj of datalistOptions) { + word = optionObj.value; + if (word === query) { + this.chooseDataListOption = word; + this.searchInput.focus(); + this.searchFunction(event.target.value.trim().toLowerCase()); + break; + } + } + } + this.chooseDataListOption = query; + this.searchSuggestions(this.trie.autoComplete(String(query))); + } else if (event.type === "keypress") { + if (this.timer) { + clearTimeout(this.timer); + } + // Enter button clicked + if (event.keyCode === 13) { + this.searchFunction(event.target.value.trim().toLowerCase()); + } else { + // Resetting the auto timmer search + this.timer = setTimeout(function() { + gSearchResultsPane.searchFunction(event.target.value.trim().toLowerCase()); + }, 600); + } + } else if (event.type === "click") { + // When close button is clicked reseting to initial state + event.preventDefault(); + this.searchInput.value = ""; + this.searchFunction(this.searchInput.value); + } else if (event.type === "command") { + this.searchInput(event); + } else if (event.type === "mouseenter" && + event.target.classList.contains("search-tooltip")) { // Inversing the position of the tooltip so it does not get in the way of content // Inverts its potion when mouse hovers/enters the tooptip area // Note: When searching for Google, Yahoo the tooltip is off ask for help. let parent = event.target; let child = parent.firstChild; if (child.classList.contains("search-tooltip-inner-reverse")) { child.classList.remove("search-tooltip-inner-reverse"); @@ -124,16 +213,20 @@ var gSearchResultsPane = { * 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) { + if (this.initialSearch) { + this.addToTries(textSearch); + } + 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) { @@ -191,18 +284,18 @@ var gSearchResultsPane = { }, /** * 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(); + searchFunction(query) { + // let query = event.target.value.trim().toLowerCase(); this.findSelection.removeAllRanges(); // Remove all search tooltips that were created let searchTooltips = Array.from(document.querySelectorAll(".search-tooltip")); for (let searchTooltip of searchTooltips) { searchTooltip.parentElement.classList.remove("search-tooltip-parent"); searchTooltip.removeEventListener("mouseenter", this); searchTooltip.remove(); @@ -255,19 +348,30 @@ var gSearchResultsPane = { } else { // Creating tooltips for all the instances found for (let obj of this.listSearchTooltips) { this.createSearchTooltip(obj, query); } } } else { this.searchResultsCategory.hidden = true; + // Removing the no query found message document.getElementById("sorry-message").textContent = ""; // Going back to General when cleared gotoPref("paneGeneral"); + // this.chooseDataListOption = ""; // Reseting word choosen + // Removing the close button image and its overlay in searchInput element + let overlayClose = document.getElementById("close-button-overlay"); + overlayClose.hidden = true; + overlayClose.removeEventListener("click", this); + this.classManipulation(false, "closeButton"); + } + + if (this.initialSearch) { + this.initialSearch = false; } }, /** * Finding leaf nodes and checking their content for words to search, * It is a recursive function * * @param Node nodeObject @@ -298,29 +402,39 @@ var gSearchResultsPane = { 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); - // Creating tooltips for buttons + // Searching some elements, such as xul:button, have a "label" attribute that contains the user-visible text. + let labelContent = nodeObject.getAttribute("label"); + if (labelContent) { + if (this.initialSearch) { + this.addToTries(labelContent); + } + + labelResult = this.stringMatchesFilters(labelContent, searchPhrase); + // Creating tooltips for buttons if (labelResult && nodeObject.tagName == "button") { this.listSearchTooltips.push(nodeObject); } } - // 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); - // Creating tooltips for buttons + // Searching some elements, such as xul:label, store their user-visible text in a "value" attribute. + let valueContent = nodeObject.getAttribute("value"); + if (valueContent) { + if (this.initialSearch) { + this.addToTries(valueContent); + } + + valueResult = this.stringMatchesFilters(valueContent, searchPhrase); + // Creating tooltips for buttons if (valueResult && nodeObject.tagName == "button") { this.listSearchTooltips.push(nodeObject); } } if (nodeObject.tagName == "button" && (labelResult || valueResult)){ nodeObject.setAttribute("highlightable", "true"); } @@ -374,10 +488,47 @@ var gSearchResultsPane = { searchContent.setAttribute("class", "search-tooltip-inner"); searchContent.textContent = searchPhrase; searchTooltip.appendChild(searchContent); currentNode.parentElement.insertBefore(searchTooltip, currentNode); searchTooltip.addEventListener("mouseenter", this); + }, + + /** + * Dynamically repopulating the datalist options for #searchInput dropdown options + * + * @param Array of Strings listSuggestions + * List of autocompleted & related words + */ + searchSuggestions(listSuggestions) { + let dataList = document.getElementById("searchSuggestions"); + // Removing old options + dataList.innerHTML = ""; + // Creating new options + for (let word of listSuggestions) { + let option = document.createElementNS("http://www.w3.org/1999/xhtml", "option"); + option.setAttribute("value", word); + dataList.appendChild(option); + } + }, + + /** + * Adding each word to the tries graph + * + * @param String phrase + * List of words to add to tries graph + */ + addToTries(phrase) { + // .replace is removing all special characters + // then removing the stop words + // then splitting it into list of words + let listOfWords = phrase.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, "").removeStopWords().split(" "); + for (let i = 0; i < listOfWords.length; i++) { + // Might need a better way to check if it is not just numbers + if (isNaN(listOfWords[i])) { + this.trie.insert(listOfWords[i].toLowerCase()); + } + } } }
--- a/browser/components/preferences/in-content/jar.mn +++ b/browser/components/preferences/in-content/jar.mn @@ -9,8 +9,9 @@ browser.jar: 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 + content/browser/preferences/in-content/tries.js
--- 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/tries.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"/> @@ -166,19 +167,23 @@ Remove this keyset once bug 1094240 ("disablefastfind" attribute broken in e10s mode) is fixed. --> <key key="&focusSearch1.key;" modifiers="accel" id="focusSearch1" oncommand=";"/> </keyset> <html:a class="help-button" target="_blank" aria-label="&helpButton2.label;">&helpButton2.label;</html:a> <vbox class="main-content" flex="1"> - <hbox pack="end"> - <textbox type="search" id="searchInput" placeholder="&searchInput.label;" hidden="true"/> + + <hbox class="search-input-hbox" pack="end"> + <html:input type="search" class="searchInput" id="searchInput" placeholder="&searchInput.label;" list="searchSuggestions" mozNoFilter="true" hidden="true" /> + <div id="close-button-overlay"></div> + <html:datalist id="searchSuggestions" 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
--- a/browser/components/preferences/in-content/tests/browser_search_within_preferences.js +++ b/browser/components/preferences/in-content/tests/browser_search_within_preferences.js @@ -1,12 +1,20 @@ /* * This file contains tests for the Preferences search bar. */ +function triggerSearch(nodeObject, searchPhrase) { + nodeObject.focus(); + nodeObject.value = searchPhrase; + let enterButton = document.createEvent("KeyboardEvent"); + enterButton.initKeyEvent("keypress", true, true, window, false, false, false, false, 13, 0); + nodeObject.dispatchEvent(enterButton); +} + /** * 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"); @@ -35,33 +43,31 @@ add_task(function*() { * 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() + triggerSearch(searchInput, ""); + triggerSearch(searchInput, "password"); 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() + triggerSearch(searchInput, ""); // 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"); @@ -74,34 +80,32 @@ add_task(function*() { /** * 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() + triggerSearch(searchInput, ""); + triggerSearch(searchInput, "password"); 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() + triggerSearch(searchInput, ""); // 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" @@ -126,25 +130,23 @@ 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() + triggerSearch(searchInput, ""); + triggerSearch(searchInput, "coach"); is_element_visible(noResultsEl, "Should be in search results"); // Takes search off - searchInput.value = ""; - searchInput.doCommand() + triggerSearch(searchInput, ""); 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 @@ -152,21 +154,19 @@ add_task(function*() { 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() + triggerSearch(searchInput, ""); + triggerSearch(searchInput, "password"); // Takes search off - searchInput.value = ""; - searchInput.doCommand() + triggerSearch(searchInput, ""); // Checks if back to normal is_element_visible(generalPane, "Should be in generalPane"); yield BrowserTestUtils.removeTab(gBrowser.selectedTab); });
new file mode 100644 --- /dev/null +++ b/browser/components/preferences/in-content/tries.js @@ -0,0 +1,202 @@ +var Trie = function() { + this.wordCount = 0; + this.children = []; + this.word = null; + this.relatedWords = []; +} + +Trie.prototype.insert = function(word) { + let node = this; + if (word === "") { + return; + } + for (let character of word.split("")) { + if (node.children[character] === undefined || node.children[character] === null) { + node.children[character] = new Trie(); + } + node = node.children[character]; + } + node.wordCount++; + node.word = word; +} + +Trie.prototype.completeWord = function(partialWord) { + let node = this; + let listOfWords = []; + let child = null; + + if (node.wordCount > 0) { + listOfWords.push(partialWord); + } + for (let childNode in node.children) { + child = node.children[childNode]; + listOfWords = listOfWords.concat(child.completeWord(partialWord + childNode)); + } + return listOfWords; +} + +Trie.prototype.listOfWords = function() { + let node = this; + if (node === undefined || node === null) { + return []; + } + let wordsList = []; + let child = null; + if (node.word != null || node.word != undefined) { + wordsList.push(node.word); + wordsList = wordsList.concat(node.relatedWords); + } + for (let char in node.children) { + child = node.children[char]; + wordsList = wordsList.concat(child.listOfWords()); + } + return wordsList; +} + +Trie.prototype.autoComplete = function(partialWord) { + let node = this; + let typos = node.levenshteinDistance(partialWord, 1); + let arrayTypos = firstElement(typos); + + for (let char of partialWord) { + if (node.children[char] === undefined || node.children[char] === null) { + return new Set([].concat(arrayTypos)); + } + node = node.children[char]; + } + // return node.completeWord(partialWord); + let complete = node.listOfWords(); + let fullList = new Set(complete.concat(arrayTypos)); + fullList.delete(partialWord); + return fullList; +} + +Trie.prototype.addRelatedWords = function(findWord, addWord, swapCall = true) { + let node = this; + if (swapCall) { + this.addRelatedWords(addWord, findWord, false); + } + + for (let char of findWord) { + if (node.children[char] != undefined || node.children[char] != null) { + node = node.children[char]; + } else { + return; + } + } + + if (node != undefined || node != null) { + node.relatedWords.push(addWord); + } +} + +Trie.prototype.levenshteinHelper = function(letter, word, previousRow, results, maxCost) { + let node = this; + let columns = word.length + 1; + let currentRow = [previousRow[0] + 1]; + let insertCost = null, deleteCost = null, replaceCost = null; + let child = null; + + for (let i = 1; i < columns; i++) { + insertCost = currentRow[i - 1] + 1; + deleteCost = previousRow[i] + 1; + + if (word[i - 1] != letter) { + replaceCost = previousRow[i - 1] + 1; + } else { + replaceCost = previousRow[i - 1]; + } + + currentRow.push(Math.min(insertCost, deleteCost, replaceCost)); + } + + if (currentRow[currentRow.length - 1] <= maxCost && node.wordCount > 0) { + results.push([node.word, currentRow[currentRow.length - 1]]); + } + + if (Math.min.apply(null, currentRow) <= maxCost) { + for (let character in node.children) { + child = node.children[character]; + child.levenshteinHelper(character, word, currentRow, results, maxCost); + } + } + return results; +} + +// wrote it with the help of http://stevehanov.ca/blog/index.php?id=114 +Trie.prototype.levenshteinDistance = function(word, maxCost) { + let node = this; + let currentRow = Array.from(Array(word.length + 1).keys()); + let results = []; + let child = null; + + for (let character in node.children) { + child = node.children[character] + child.levenshteinHelper(character, word, currentRow, results, maxCost); + } + return results; +} + +function firstElement(arry) { + let temp = []; + for (let i = 0; i < arry.length; i++) { + temp.push(arry[i][0]); + } + return temp; +} + +String.isStopWord = function(word) { + let regex = new RegExp("\\b" + word + "\\b", "i"); + if (stopWords.search(regex) < 0) { + return false; + } + return true; +} + +String.prototype.removeStopWords = function() { + let words = new Array(); + + this.replace(/\b[\w]+\b/g, function($0) { + if (!String.isStopWord($0)) { + words[words.length] = $0.trim(); + } + }); + return words.join(" "); +} + +// Source : https://github.com/mirzaasif/JS-StopWord/blob/master/StopWord.js +var stopWords = "a,able,about,above,abst,accordance,according,accordingly,across,act,actually,added,adj,\ +affected,affecting,affects,after,afterwards,again,against,ah,all,almost,alone,along,already,also,although,\ +always,am,among,amongst,an,and,announce,another,any,anybody,anyhow,anymore,anyone,anything,anyway,anyways,\ +anywhere,apparently,approximately,are,aren,arent,arise,around,as,aside,ask,asking,at,auth,available,away,awfully,\ +b,back,be,became,because,become,becomes,becoming,been,before,beforehand,begin,beginning,beginnings,begins,behind,\ +being,believe,below,beside,besides,between,beyond,biol,both,brief,briefly,but,by,c,ca,came,can,cannot,can't,cause,causes,\ +certain,certainly,co,com,come,comes,contain,containing,contains,could,couldnt,d,date,did,didn't,different,do,does,doesn't,\ +doing,done,don't,down,downwards,due,during,e,each,ed,edu,effect,eg,eight,eighty,either,else,elsewhere,end,ending,enough,\ +especially,et,et-al,etc,even,ever,every,everybody,everyone,everything,everywhere,ex,except,f,far,few,ff,fifth,first,five,fix,\ +followed,following,follows,for,former,formerly,forth,found,four,from,further,furthermore,g,gave,get,gets,getting,give,given,gives,\ +giving,go,goes,gone,got,gotten,h,had,happens,hardly,has,hasn't,have,haven't,having,he,hed,hence,her,here,hereafter,hereby,herein,\ +heres,hereupon,hers,herself,hes,hi,hid,him,himself,his,hither,home,how,howbeit,however,hundred,i,id,ie,if,i'll,im,immediate,\ +immediately,importance,important,in,inc,indeed,index,information,instead,into,invention,inward,is,isn't,it,itd,it'll,its,itself,\ +i've,j,just,k,keep,keeps,kept,kg,km,know,known,knows,l,largely,last,lately,later,latter,latterly,least,less,lest,let,lets,like,\ +liked,likely,line,little,'ll,look,looking,looks,ltd,m,made,mainly,make,makes,many,may,maybe,me,mean,means,meantime,meanwhile,\ +merely,mg,might,million,miss,ml,more,moreover,most,mostly,mr,mrs,much,mug,must,my,myself,n,na,name,namely,nay,nd,near,nearly,\ +necessarily,necessary,need,needs,neither,never,nevertheless,new,next,nine,ninety,no,nobody,non,none,nonetheless,noone,nor,\ +normally,nos,not,noted,nothing,now,nowhere,o,obtain,obtained,obviously,of,off,often,oh,ok,okay,old,omitted,on,once,one,ones,\ +only,onto,or,ord,other,others,otherwise,ought,our,ours,ourselves,out,outside,over,overall,owing,own,p,page,pages,part,\ +particular,particularly,past,per,perhaps,placed,please,plus,poorly,possible,possibly,potentially,pp,predominantly,present,\ +previously,primarily,probably,promptly,proud,provides,put,q,que,quickly,quite,qv,r,ran,rather,rd,re,readily,really,recent,\ +recently,ref,refs,regarding,regardless,regards,related,relatively,research,respectively,resulted,resulting,results,right,run,s,\ +said,same,saw,say,saying,says,sec,section,see,seeing,seem,seemed,seeming,seems,seen,self,selves,sent,seven,several,shall,she,shed,\ +she'll,shes,should,shouldn't,show,showed,shown,showns,shows,significant,significantly,similar,similarly,since,six,slightly,so,\ +some,somebody,somehow,someone,somethan,something,sometime,sometimes,somewhat,somewhere,soon,sorry,specifically,specified,specify,\ +specifying,still,stop,strongly,sub,substantially,successfully,such,sufficiently,suggest,sup,sure,t,take,taken,taking,tell,tends,\ +th,than,thank,thanks,thanx,that,that'll,thats,that've,the,their,theirs,them,themselves,then,thence,there,thereafter,thereby,\ +thered,therefore,therein,there'll,thereof,therere,theres,thereto,thereupon,there've,these,they,theyd,they'll,theyre,they've,\ +think,this,those,thou,though,thoughh,thousand,throug,through,throughout,thru,thus,til,tip,to,together,too,took,toward,towards,\ +tried,tries,truly,try,trying,ts,twice,two,u,un,under,unfortunately,unless,unlike,unlikely,until,unto,up,upon,ups,us,use,used,\ +useful,usefully,usefulness,uses,using,usually,v,value,various,'ve,very,via,viz,vol,vols,vs,w,want,wants,was,wasn't,way,we,wed,\ +welcome,we'll,went,were,weren't,we've,what,whatever,what'll,whats,when,whence,whenever,where,whereafter,whereas,whereby,wherein,\ +wheres,whereupon,wherever,whether,which,while,whim,whither,who,whod,whoever,whole,who'll,whom,whomever,whos,whose,why,widely,\ +willing,wish,with,within,without,won't,words,world,would,wouldn't,www,x,y,yes,yet,you,youd,you'll,your,youre,yours,yourself,\ +yourselves,you've,z,zero";
--- a/browser/themes/shared/incontentprefs/preferences.inc.css +++ b/browser/themes/shared/incontentprefs/preferences.inc.css @@ -617,8 +617,41 @@ description > html|a { bottom: -7px; top: auto; } /* Styling for parents node of search tooltip */ .search-tooltip-parent { position: relative; } + +.searchInput { + /* backround-image: search-icon image, seach-close-button image + * http://stackoverflow.com/questions/6258521/clear-icon-inside-input-text + */ + background-image: url(chrome://browser/skin/search-indicator-magnifying-glass.svg),url(chrome://global/skin/icons/searchfield-cancel.svg); + border: 1px solid #999; + padding: 4px 20px 4px 20px; + border-radius: 3px; + background-repeat: no-repeat, no-repeat; + background-position: left 4px center,right -14px center; + background-size: 15px 15px, 12px 12px; + font-size: 12px; +} + +.searchInput.closeButton { + background-position: left 4px center, right 5px center; +} + +.search-input-hbox { + position: relative; +} + +#close-button-overlay { + z-index: 1000; + position: absolute; + width: 14px; + height: 14px; + right: 5px; + bottom: 5px; + background-color: transparent; + cursor: pointer; +}
--- a/toolkit/themes/shared/in-content/common.inc.css +++ b/toolkit/themes/shared/in-content/common.inc.css @@ -88,16 +88,17 @@ xul|caption > xul|label { } *|*.main-content { padding-top: 40px; padding-inline-end: 44px; /* compensate the 4px margin of child elements */ padding-bottom: 48px; padding-inline-start: 48px; overflow: auto; + position: relative; /* Search Bar alignment with header in about:preferences */ } xul|prefpane > xul|*.content-box { overflow: visible; } /* groupboxes */