зеркало из https://github.com/mozilla/gecko-dev.git
Bug 392143 - show keywords as url bar autocomplete choices. r=dietrich, r=gavin
This commit is contained in:
Родитель
87d96aafd9
Коммит
1bef7eb888
|
@ -1039,6 +1039,13 @@ toolbar[iconsize="small"] #paste-button[disabled="true"] {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ac-result-type-keyword,
|
||||||
|
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
|
||||||
|
list-style-image: url(chrome://browser/skin/Search-glass.png);
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.ac-result-type-tag,
|
.ac-result-type-tag,
|
||||||
.autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
|
.autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
|
||||||
list-style-image: url("chrome://browser/skin/places/tag.png");
|
list-style-image: url("chrome://browser/skin/places/tag.png");
|
||||||
|
|
|
@ -993,6 +993,13 @@ statusbarpanel#statusbar-display {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ac-result-type-keyword,
|
||||||
|
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
|
||||||
|
list-style-image: url(chrome://global/skin/icons/search-textbox.png);
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
richlistitem[selected="true"][current="true"] > hbox > .ac-result-type-bookmark,
|
richlistitem[selected="true"][current="true"] > hbox > .ac-result-type-bookmark,
|
||||||
.autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
|
.autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
|
||||||
list-style-image: url("chrome://browser/skin/places/star-icons.png");
|
list-style-image: url("chrome://browser/skin/places/star-icons.png");
|
||||||
|
|
|
@ -1184,6 +1184,14 @@ statusbarpanel#statusbar-display {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ac-result-type-keyword,
|
||||||
|
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
|
||||||
|
list-style-image: url(chrome://browser/skin/Search-glass.png);
|
||||||
|
-moz-image-region: rect(0px 32px 16px 16px);
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.ac-result-type-tag,
|
.ac-result-type-tag,
|
||||||
.autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
|
.autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
|
||||||
list-style-image: url("chrome://browser/skin/places/tag.png");
|
list-style-image: url("chrome://browser/skin/places/tag.png");
|
||||||
|
|
|
@ -645,6 +645,7 @@ protected:
|
||||||
nsCOMPtr<mozIStorageStatement> mDBAutoCompleteQuery; // kAutoCompleteIndex_* results
|
nsCOMPtr<mozIStorageStatement> mDBAutoCompleteQuery; // kAutoCompleteIndex_* results
|
||||||
nsCOMPtr<mozIStorageStatement> mDBPreviousQuery; // kAutoCompleteIndex_* results
|
nsCOMPtr<mozIStorageStatement> mDBPreviousQuery; // kAutoCompleteIndex_* results
|
||||||
nsCOMPtr<mozIStorageStatement> mDBAdaptiveQuery; // kAutoCompleteIndex_* results
|
nsCOMPtr<mozIStorageStatement> mDBAdaptiveQuery; // kAutoCompleteIndex_* results
|
||||||
|
nsCOMPtr<mozIStorageStatement> mDBKeywordQuery; // kAutoCompleteIndex_* results
|
||||||
nsCOMPtr<mozIStorageStatement> mDBFeedbackIncrease;
|
nsCOMPtr<mozIStorageStatement> mDBFeedbackIncrease;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -667,6 +668,8 @@ protected:
|
||||||
PRInt32 mAutoCompleteSearchTimeout;
|
PRInt32 mAutoCompleteSearchTimeout;
|
||||||
nsCOMPtr<nsITimer> mAutoCompleteTimer;
|
nsCOMPtr<nsITimer> mAutoCompleteTimer;
|
||||||
|
|
||||||
|
// Original search string for case-sensitive usage
|
||||||
|
nsString mOrigSearchString;
|
||||||
// Search string and tokens for case-insensitive matching
|
// Search string and tokens for case-insensitive matching
|
||||||
nsString mCurrentSearchString;
|
nsString mCurrentSearchString;
|
||||||
nsStringArray mCurrentSearchTokens;
|
nsStringArray mCurrentSearchTokens;
|
||||||
|
@ -693,12 +696,14 @@ protected:
|
||||||
nsresult AutoCompleteFullHistorySearch(PRBool* aHasMoreResults);
|
nsresult AutoCompleteFullHistorySearch(PRBool* aHasMoreResults);
|
||||||
nsresult AutoCompletePreviousSearch();
|
nsresult AutoCompletePreviousSearch();
|
||||||
nsresult AutoCompleteAdaptiveSearch();
|
nsresult AutoCompleteAdaptiveSearch();
|
||||||
|
nsresult AutoCompleteKeywordSearch();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query type passed to AutoCompleteProcessSearch to determine what style to
|
* Query type passed to AutoCompleteProcessSearch to determine what style to
|
||||||
* use and if results should be filtered
|
* use and if results should be filtered
|
||||||
*/
|
*/
|
||||||
enum QueryType {
|
enum QueryType {
|
||||||
|
QUERY_KEYWORD,
|
||||||
QUERY_ADAPTIVE,
|
QUERY_ADAPTIVE,
|
||||||
QUERY_FULL
|
QUERY_FULL
|
||||||
};
|
};
|
||||||
|
|
|
@ -286,6 +286,24 @@ nsNavHistory::CreateAutoCompleteQueries()
|
||||||
rv = mDBConn->CreateStatement(sql, getter_AddRefs(mDBAdaptiveQuery));
|
rv = mDBConn->CreateStatement(sql, getter_AddRefs(mDBAdaptiveQuery));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
sql = NS_LITERAL_CSTRING(
|
||||||
|
"SELECT REPLACE(s.url, '%s', ?2) search_url, h.title, IFNULL(f.url, "
|
||||||
|
"(SELECT f.url "
|
||||||
|
"FROM moz_places r "
|
||||||
|
"JOIN moz_favicons f ON f.id = r.favicon_id "
|
||||||
|
"WHERE r.rev_host = s.rev_host "
|
||||||
|
"ORDER BY r.frecency DESC LIMIT 1)), "
|
||||||
|
"b.parent, b.title, NULL "
|
||||||
|
"FROM moz_keywords k "
|
||||||
|
"JOIN moz_bookmarks b ON b.keyword_id = k.id "
|
||||||
|
"JOIN moz_places s ON s.id = b.fk "
|
||||||
|
"LEFT OUTER JOIN moz_places h ON h.url = search_url "
|
||||||
|
"LEFT OUTER JOIN moz_favicons f ON f.id = h.favicon_id "
|
||||||
|
"WHERE LOWER(k.keyword) = LOWER(?1) "
|
||||||
|
"ORDER BY h.frecency DESC");
|
||||||
|
rv = mDBConn->CreateStatement(sql, getter_AddRefs(mDBKeywordQuery));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
sql = NS_LITERAL_CSTRING(
|
sql = NS_LITERAL_CSTRING(
|
||||||
// Leverage the PRIMARY KEY (place_id, input) to insert/update entries
|
// Leverage the PRIMARY KEY (place_id, input) to insert/update entries
|
||||||
"INSERT OR REPLACE INTO moz_inputhistory "
|
"INSERT OR REPLACE INTO moz_inputhistory "
|
||||||
|
@ -340,6 +358,12 @@ nsNavHistory::PerformAutoComplete()
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
// Only do some extra searches on the first chunk
|
// Only do some extra searches on the first chunk
|
||||||
if (!mCurrentChunkOffset) {
|
if (!mCurrentChunkOffset) {
|
||||||
|
// Only show keywords if there's a search
|
||||||
|
if (mCurrentSearchTokens.Count()) {
|
||||||
|
rv = AutoCompleteKeywordSearch();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
// Get adaptive results first
|
// Get adaptive results first
|
||||||
rv = AutoCompleteAdaptiveSearch();
|
rv = AutoCompleteAdaptiveSearch();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
@ -444,10 +468,12 @@ nsNavHistory::StartSearch(const nsAString & aSearchString,
|
||||||
PRUint32 prevMatchCount = mCurrentResultURLs.Count();
|
PRUint32 prevMatchCount = mCurrentResultURLs.Count();
|
||||||
nsAutoString prevSearchString(mCurrentSearchString);
|
nsAutoString prevSearchString(mCurrentSearchString);
|
||||||
|
|
||||||
|
// Keep a copy of the original search for case-sensitive usage
|
||||||
|
mOrigSearchString = aSearchString;
|
||||||
|
// Remove whitespace, see bug #392141 for details
|
||||||
|
mOrigSearchString.Trim(" \r\n\t\b");
|
||||||
// Copy the input search string for case-insensitive search
|
// Copy the input search string for case-insensitive search
|
||||||
ToLowerCase(aSearchString, mCurrentSearchString);
|
ToLowerCase(mOrigSearchString, mCurrentSearchString);
|
||||||
// remove whitespace, see bug #392141 for details
|
|
||||||
mCurrentSearchString.Trim(" \r\n\t\b");
|
|
||||||
|
|
||||||
mCurrentListener = aListener;
|
mCurrentListener = aListener;
|
||||||
|
|
||||||
|
@ -608,6 +634,34 @@ nsNavHistory::AddSearchToken(nsAutoString &aToken)
|
||||||
mCurrentSearchTokens.AppendString(aToken);
|
mCurrentSearchTokens.AppendString(aToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsNavHistory::AutoCompleteKeywordSearch()
|
||||||
|
{
|
||||||
|
mozStorageStatementScoper scope(mDBKeywordQuery);
|
||||||
|
|
||||||
|
// Get the keyword parameters to replace the %s in the keyword search
|
||||||
|
nsCAutoString params;
|
||||||
|
PRInt32 paramPos = mOrigSearchString.FindChar(' ') + 1;
|
||||||
|
// Make sure to escape them as if they were the query in a url, so " " become
|
||||||
|
// "+"; "+" become "%2B"; non-ascii escaped
|
||||||
|
NS_Escape(NS_ConvertUTF16toUTF8(Substring(mOrigSearchString, paramPos)),
|
||||||
|
params, url_XPAlphas);
|
||||||
|
|
||||||
|
// The first search term might be a keyword
|
||||||
|
const nsAString &keyword = Substring(mOrigSearchString, 0,
|
||||||
|
paramPos ? paramPos - 1 : mOrigSearchString.Length());
|
||||||
|
nsresult rv = mDBKeywordQuery->BindStringParameter(0, keyword);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = mDBKeywordQuery->BindUTF8StringParameter(1, params);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = AutoCompleteProcessSearch(mDBKeywordQuery, QUERY_KEYWORD);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsNavHistory::AutoCompleteAdaptiveSearch()
|
nsNavHistory::AutoCompleteAdaptiveSearch()
|
||||||
{
|
{
|
||||||
|
@ -734,6 +788,17 @@ nsNavHistory::AutoCompleteProcessSearch(mozIStorageStatement* aQuery,
|
||||||
|
|
||||||
nsString style;
|
nsString style;
|
||||||
switch (aType) {
|
switch (aType) {
|
||||||
|
case QUERY_KEYWORD: {
|
||||||
|
// If we don't have a title, then we must have a keyword, so let the
|
||||||
|
// UI know it's a keyword; otherwise, we found an exact page match,
|
||||||
|
// so just show the page like a regular result
|
||||||
|
if (entryTitle.IsEmpty())
|
||||||
|
style = NS_LITERAL_STRING("keyword");
|
||||||
|
else
|
||||||
|
title = entryTitle;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case QUERY_FULL: {
|
case QUERY_FULL: {
|
||||||
// If we get any results, there's potentially another chunk to proces
|
// If we get any results, there's potentially another chunk to proces
|
||||||
if (aHasMoreResults)
|
if (aHasMoreResults)
|
||||||
|
@ -776,10 +841,15 @@ nsNavHistory::AutoCompleteProcessSearch(mozIStorageStatement* aQuery,
|
||||||
|
|
||||||
// Tags have a special style to show a tag icon; otherwise, style the
|
// Tags have a special style to show a tag icon; otherwise, style the
|
||||||
// bookmarks that aren't feed items and feed URIs as bookmark
|
// bookmarks that aren't feed items and feed URIs as bookmark
|
||||||
style = showTags ? NS_LITERAL_STRING("tag") : (parentId &&
|
if (style.IsEmpty()) {
|
||||||
!mLivemarkFeedItemIds.Get(parentId, &dummy)) ||
|
if (showTags)
|
||||||
mLivemarkFeedURIs.Get(escapedEntryURL, &dummy) ?
|
style = NS_LITERAL_STRING("tag");
|
||||||
NS_LITERAL_STRING("bookmark") : NS_LITERAL_STRING("favicon");
|
else if ((parentId && !mLivemarkFeedItemIds.Get(parentId, &dummy)) ||
|
||||||
|
mLivemarkFeedURIs.Get(escapedEntryURL, &dummy))
|
||||||
|
style = NS_LITERAL_STRING("bookmark");
|
||||||
|
else
|
||||||
|
style = NS_LITERAL_STRING("favicon");
|
||||||
|
}
|
||||||
|
|
||||||
// Get the URI for the favicon
|
// Get the URI for the favicon
|
||||||
nsCAutoString faviconSpec;
|
nsCAutoString faviconSpec;
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Places Test Code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Edward Lee <edward.lee@engineering.uiuc.edu>.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for bug 392143 that puts keyword results into the autocomplete. Makes
|
||||||
|
* sure that multiple parameter queries get spaces converted to +, + converted
|
||||||
|
* to %2B, non-ascii become escaped, and pages in history that match the
|
||||||
|
* keyword uses the page's title.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Details for the keyword bookmark
|
||||||
|
let keyBase = "http://abc/?search=";
|
||||||
|
let keyKey = "key";
|
||||||
|
|
||||||
|
let unescaped = "ユニコード";
|
||||||
|
let pageInHistory = "ThisPageIsInHistory";
|
||||||
|
|
||||||
|
// Define some shared uris and titles (each page needs its own uri)
|
||||||
|
let kURIs = [
|
||||||
|
keyBase + "%s",
|
||||||
|
keyBase + "term",
|
||||||
|
keyBase + "multi+word",
|
||||||
|
keyBase + "blocking%2B",
|
||||||
|
keyBase + unescaped,
|
||||||
|
keyBase + pageInHistory,
|
||||||
|
];
|
||||||
|
let kTitles = [
|
||||||
|
"Generic page title",
|
||||||
|
"Keyword title",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Add the keyword bookmark
|
||||||
|
addPageBook(0, 0, 1, [], keyKey);
|
||||||
|
// Add in the "fake pages" for keyword searches
|
||||||
|
gPages[1] = [1,1];
|
||||||
|
gPages[2] = [2,1];
|
||||||
|
gPages[3] = [3,1];
|
||||||
|
gPages[4] = [4,1];
|
||||||
|
// Add a page into history
|
||||||
|
addPageBook(5, 0);
|
||||||
|
|
||||||
|
// Provide for each test: description; search terms; array of gPages indices of
|
||||||
|
// pages that should match; optional function to be run before the test
|
||||||
|
let gTests = [
|
||||||
|
["0: Plain keyword query",
|
||||||
|
keyKey + " term", [1]],
|
||||||
|
["1: Multi-word keyword query",
|
||||||
|
keyKey + " multi word", [2]],
|
||||||
|
["2: Keyword query with +",
|
||||||
|
keyKey + " blocking+", [3]],
|
||||||
|
["3: Unescaped term in query",
|
||||||
|
keyKey + " " + unescaped, [4]],
|
||||||
|
["4: Keyword that happens to match a page",
|
||||||
|
keyKey + " " + pageInHistory, [5]],
|
||||||
|
];
|
|
@ -1152,6 +1152,7 @@
|
||||||
<xul:description anonid="title" class="ac-normal-text ac-comment" xbl:inherits="selected"/>
|
<xul:description anonid="title" class="ac-normal-text ac-comment" xbl:inherits="selected"/>
|
||||||
<xul:hbox anonid="extra-box" class="ac-extra" align="center" hidden="true">
|
<xul:hbox anonid="extra-box" class="ac-extra" align="center" hidden="true">
|
||||||
<xul:image class="ac-result-type-tag"/>
|
<xul:image class="ac-result-type-tag"/>
|
||||||
|
<xul:label class="ac-normal-text ac-comment" xbl:inherits="selected" value=":"/>
|
||||||
<xul:description anonid="extra" class="ac-normal-text ac-comment" xbl:inherits="selected"/>
|
<xul:description anonid="extra" class="ac-normal-text ac-comment" xbl:inherits="selected"/>
|
||||||
</xul:hbox>
|
</xul:hbox>
|
||||||
</xul:hbox>
|
</xul:hbox>
|
||||||
|
@ -1385,6 +1386,8 @@
|
||||||
if (type == "tag") {
|
if (type == "tag") {
|
||||||
// Configure the extra box for tags display
|
// Configure the extra box for tags display
|
||||||
this._extraBox.hidden = false;
|
this._extraBox.hidden = false;
|
||||||
|
this._extraBox.childNodes[0].hidden = false;
|
||||||
|
this._extraBox.childNodes[1].hidden = true;
|
||||||
this._extraBox.flex = 1;
|
this._extraBox.flex = 1;
|
||||||
this._extraBox.pack = "end";
|
this._extraBox.pack = "end";
|
||||||
|
|
||||||
|
@ -1400,6 +1403,23 @@
|
||||||
|
|
||||||
// Treat tagged matches as bookmarks for the star
|
// Treat tagged matches as bookmarks for the star
|
||||||
type = "bookmark";
|
type = "bookmark";
|
||||||
|
} else if (type == "keyword") {
|
||||||
|
// Configure the extra box for keyword display
|
||||||
|
this._extraBox.hidden = false;
|
||||||
|
this._extraBox.childNodes[0].hidden = true;
|
||||||
|
this._extraBox.childNodes[1].hidden = false;
|
||||||
|
this._extraBox.flex = 0;
|
||||||
|
this._extraBox.pack = "start";
|
||||||
|
|
||||||
|
// Put the parameters next to the title if we have any
|
||||||
|
let search = this.getAttribute("text");
|
||||||
|
let params = search.substr(search.indexOf(' ') + 1);
|
||||||
|
|
||||||
|
// Emphasize the keyword parameters
|
||||||
|
this._setUpDescription(this._extra, params);
|
||||||
|
|
||||||
|
// Don't emphasize keyword searches in the title or url
|
||||||
|
this.setAttribute("text", "");
|
||||||
} else {
|
} else {
|
||||||
// Hide the title's extra box if we don't need extra stuff
|
// Hide the title's extra box if we don't need extra stuff
|
||||||
this._extraBox.hidden = true;
|
this._extraBox.hidden = true;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче