Bug 392143 - show keywords as url bar autocomplete choices. r=dietrich, r=gavin

This commit is contained in:
Edward Lee 2008-07-17 04:48:40 -07:00
Родитель 87d96aafd9
Коммит 1bef7eb888
7 изменённых файлов: 212 добавлений и 7 удалений

Просмотреть файл

@ -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;