зеркало из https://github.com/mozilla/pjs.git
Bug 320181 r=annie.sullivan Rewrite autocomplete code for places.
This commit is contained in:
Родитель
ab96ea7457
Коммит
2bacb3d7d4
|
@ -98,7 +98,6 @@
|
|||
#define PREF_BRANCH_BASE "browser."
|
||||
#define PREF_BROWSER_HISTORY_EXPIRE_DAYS "history_expire_days"
|
||||
#define PREF_AUTOCOMPLETE_ONLY_TYPED "urlbar.matchOnlyTyped"
|
||||
#define PREF_AUTOCOMPLETE_MAX_COUNT "urlbar.autocomplete.maxCount"
|
||||
#define PREF_AUTOCOMPLETE_ENABLED "urlbar.autocomplete.enabled"
|
||||
|
||||
// the value of mLastNow expires every 3 seconds
|
||||
|
@ -253,13 +252,8 @@ nsNavHistory::Init()
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#endif
|
||||
|
||||
// commonly used prefixes that should be chopped off all history and input
|
||||
// urls before comparison
|
||||
mIgnoreSchemes.AppendString(NS_LITERAL_STRING("http://"));
|
||||
mIgnoreSchemes.AppendString(NS_LITERAL_STRING("https://"));
|
||||
mIgnoreSchemes.AppendString(NS_LITERAL_STRING("ftp://"));
|
||||
mIgnoreHostnames.AppendString(NS_LITERAL_STRING("www."));
|
||||
mIgnoreHostnames.AppendString(NS_LITERAL_STRING("ftp."));
|
||||
rv = InitAutoComplete();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// extract the last session ID so we know where to pick up. There is no index
|
||||
// over sessions so the naive statement "SELECT MAX(session) FROM
|
||||
|
@ -345,7 +339,6 @@ nsNavHistory::Init()
|
|||
if (pbi) {
|
||||
pbi->AddObserver(PREF_AUTOCOMPLETE_ONLY_TYPED, this, PR_FALSE);
|
||||
pbi->AddObserver(PREF_BROWSER_HISTORY_EXPIRE_DAYS, this, PR_FALSE);
|
||||
pbi->AddObserver(PREF_AUTOCOMPLETE_MAX_COUNT, this, PR_FALSE);
|
||||
}
|
||||
|
||||
observerService->AddObserver(this, gQuitApplicationMessage, PR_FALSE);
|
||||
|
@ -1091,10 +1084,13 @@ nsNavHistory::LoadPrefs()
|
|||
return NS_OK;
|
||||
|
||||
mPrefBranch->GetIntPref(PREF_BROWSER_HISTORY_EXPIRE_DAYS, &mExpireDays);
|
||||
PRBool oldCompleteOnlyTyped = mAutoCompleteOnlyTyped;
|
||||
mPrefBranch->GetBoolPref(PREF_AUTOCOMPLETE_ONLY_TYPED,
|
||||
&mAutoCompleteOnlyTyped);
|
||||
if (NS_FAILED(mPrefBranch->GetIntPref(PREF_AUTOCOMPLETE_MAX_COUNT, &mAutoCompleteMaxCount))) {
|
||||
mAutoCompleteMaxCount = 2000; // FIXME: add this to default prefs.js
|
||||
if (oldCompleteOnlyTyped != mAutoCompleteOnlyTyped) {
|
||||
// update the autocomplete statement if the option has changed.
|
||||
nsresult rv = CreateAutoCompleteQuery();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -76,10 +76,6 @@
|
|||
#include "nsNavHistoryResult.h"
|
||||
#include "nsNavHistoryQuery.h"
|
||||
|
||||
// Number of prefixes used in the autocomplete sort comparison function
|
||||
#define AUTOCOMPLETE_PREFIX_LIST_COUNT 6
|
||||
// Size of visit count boost to give to urls which are sites or paths
|
||||
#define AUTOCOMPLETE_NONPAGE_VISIT_COUNT_BOOST 5
|
||||
// set to use more optimized (in-memory database) link coloring
|
||||
//#define IN_MEMORY_LINKS
|
||||
|
||||
|
@ -88,7 +84,8 @@
|
|||
#define QUERYUPDATE_COMPLEX 2
|
||||
#define QUERYUPDATE_COMPLEX_WITH_BOOKMARKS 3
|
||||
|
||||
class AutoCompleteIntermediateResultSet;
|
||||
class AutoCompleteIntermediateResult;
|
||||
class AutoCompleteResultComparator;
|
||||
class mozIAnnotationService;
|
||||
class nsNavHistory;
|
||||
class nsNavBookmarks;
|
||||
|
@ -104,6 +101,7 @@ class nsNavHistory : public nsSupportsWeakReference,
|
|||
public nsIAutoCompleteSearch
|
||||
{
|
||||
friend class AutoCompleteIntermediateResultSet;
|
||||
friend class AutoCompleteResultComparator;
|
||||
public:
|
||||
nsNavHistory();
|
||||
|
||||
|
@ -442,9 +440,26 @@ protected:
|
|||
//
|
||||
// AutoComplete stuff
|
||||
//
|
||||
nsStringArray mIgnoreSchemes;
|
||||
nsStringArray mIgnoreHostnames;
|
||||
struct AutoCompletePrefix
|
||||
{
|
||||
AutoCompletePrefix(const nsAString& aPrefix, PRBool aSecondLevel) :
|
||||
prefix(aPrefix), secondLevel(aSecondLevel) {}
|
||||
|
||||
// The prefix, for example, "http://" or "https://www."
|
||||
nsString prefix;
|
||||
|
||||
// Set when this prefix contains a spec AND a host. For example,
|
||||
// "http://www." is a second level prefix, but "http://" is not. This
|
||||
// flag is used to exclude matches. For example, if I type "http://w"
|
||||
// I probably want it to autocomplete to sites beginning with w and
|
||||
// NOT every "www" site I've ever visited.
|
||||
PRBool secondLevel;
|
||||
};
|
||||
nsTArray<AutoCompletePrefix> mAutoCompletePrefixes;
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> mDBAutoCompleteQuery;
|
||||
nsresult InitAutoComplete();
|
||||
nsresult CreateAutoCompleteQuery();
|
||||
PRInt32 mAutoCompleteMaxCount;
|
||||
PRInt32 mExpireDays;
|
||||
PRBool mAutoCompleteOnlyTyped;
|
||||
|
@ -463,20 +478,11 @@ protected:
|
|||
nsresult AutoCompleteTypedSearch(nsIAutoCompleteSimpleResult* result);
|
||||
nsresult AutoCompleteFullHistorySearch(const nsAString& aSearchString,
|
||||
nsIAutoCompleteSimpleResult* result);
|
||||
nsresult AutoCompleteRefineHistorySearch(const nsAString& aSearchString,
|
||||
nsIAutoCompleteResult* aPreviousResult,
|
||||
nsIAutoCompleteSimpleResult* aNewResult);
|
||||
void AutoCompleteCutPrefix(nsString* aURL,
|
||||
const AutoCompleteExclude* aExclude);
|
||||
void AutoCompleteGetExcludeInfo(const nsAString& aURL,
|
||||
AutoCompleteExclude* aExclude);
|
||||
PRBool AutoCompleteCompare(const nsAString& aHistoryURL,
|
||||
const nsAString& aUserURL,
|
||||
const AutoCompleteExclude& aExclude);
|
||||
PR_STATIC_CALLBACK(int) AutoCompleteSortComparison(
|
||||
const void* match1Void,
|
||||
const void* match2Void,
|
||||
void *navHistoryVoid);
|
||||
nsresult AutoCompleteQueryOnePrefix(const nsString& aSearchString,
|
||||
const nsTArray<PRInt32>& aExcludePrefixes,
|
||||
PRInt32 aPriorityDelta,
|
||||
nsTArray<AutoCompleteIntermediateResult>* aResult);
|
||||
PRInt32 AutoCompleteGetPrefixLength(const nsString& aSpec);
|
||||
|
||||
// in nsNavHistoryQuery.cpp
|
||||
nsresult TokensToQueries(const nsTArray<QueryKeyValuePair>& aTokens,
|
||||
|
|
|
@ -38,7 +38,66 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
/**
|
||||
* Autocomplte algorithm:
|
||||
*
|
||||
* Scoring
|
||||
* -------
|
||||
* Generally ordering is by visit count. We given boosts to items that have
|
||||
* been bookmarked or typed into the address bar (as opposed to clicked links).
|
||||
* The penalties below for schemes and prefix matches also apply. We also
|
||||
* prefer paths (URLs ending in '/') and try to have shorter ones generally
|
||||
* appear first. The results are then presented in descending numeric order
|
||||
* using this score.
|
||||
*
|
||||
* Database queries
|
||||
* ----------------
|
||||
* It is tempting to do a select for "url LIKE user_input" but this is a very
|
||||
* slow query since it is brute-force over all URLs in histroy. We can,
|
||||
* however, do very efficient queries using < and > for strings. These
|
||||
* operators use the index over the URL column.
|
||||
*
|
||||
* Therefore, we try to prepend any prefixes that should be considered and
|
||||
* query for them individually. Therefore, we execute several very efficient
|
||||
* queries, score the results, and sort it.
|
||||
*
|
||||
* To limit the amount of searching we do, we ask the database to order the
|
||||
* results based on visit count for us and give us on the top N results.
|
||||
* These results will be in approximate order of score. As long as each query
|
||||
* has more results than the user is likely to see, they will not notice the
|
||||
* effects of this.
|
||||
*
|
||||
* First see if any specs match that from the beginning
|
||||
* ----------------------------------------------------
|
||||
* - If it matches the beginning of a known prefix prefix, exclude that prefix
|
||||
* when querying. We would therefore exclude "http://" and "https://" if you type
|
||||
* "ht". But match any other schemes that begin with "ht" (probably none).
|
||||
*
|
||||
* - Penalize all results. Most people don't type the scheme and don't want
|
||||
* matches like this. This will make "file://" links go below
|
||||
* "http://www.fido.com/". If one is typing the scheme "file:" for example, by
|
||||
* the time you type the colon it won't match anything else (like "http://file:")
|
||||
* and the penalty won't have any effect on the ordering (it will be applied to
|
||||
* all results).
|
||||
*
|
||||
* Try different known prefixes
|
||||
* ----------------------------
|
||||
* - Prepend each prefix, running a query. If the concatenated string is itself a
|
||||
* prefix of another known prefix (ie input is "w" and we prepend "http://", it
|
||||
* will be a prefix of "http://www."), select out that known prefix. In this
|
||||
* case we'll query for everything starting with "http://w" except things
|
||||
* starting with "http://www."
|
||||
*
|
||||
* - For each selected out prefix above, run a query but apply prefix match
|
||||
* penalty to the results. This way you'll still get "http://www." results
|
||||
* if you type "w", but they will generally be lower than "http://wookie.net/"
|
||||
* For your favorite few sites with many visits, you might still get matches
|
||||
* for "www" first, which is probably what you want for your favorite sites.
|
||||
*/
|
||||
|
||||
#include "nsNavHistory.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
#include "mozIStorageService.h"
|
||||
#include "mozIStorageConnection.h"
|
||||
|
@ -51,9 +110,49 @@
|
|||
#define NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID \
|
||||
"@mozilla.org/autocomplete/simple-result;1"
|
||||
|
||||
// Size of visit count boost to give to URLs which are sites or paths
|
||||
#define AUTOCOMPLETE_NONPAGE_VISIT_COUNT_BOOST 5
|
||||
|
||||
// Boost to give to URLs which have been typed
|
||||
#define AUTOCOMPLETE_TYPED_BOOST 5
|
||||
|
||||
// Boost to give to URLs which are bookmarked
|
||||
#define AUTOCOMPLETE_BOOKMARKED_BOOST 5
|
||||
|
||||
// Penalty to add to sites that match a prefix. For example, if I type "w"
|
||||
// we will complte on "http://www.w..." like normal. We we will also complete
|
||||
// on "http://w" which will match almost every site, but will have this penalty
|
||||
// applied so they will come later. We want a pretty big penalty so that you'll
|
||||
// only get "www" beating domain names that start with w for your very favorite
|
||||
// sites.
|
||||
#define AUTOCOMPLETE_MATCHES_PREFIX_PENALTY (-50)
|
||||
|
||||
// Penalty applied to matches that don't have prefixes applied. See
|
||||
// discussion above.
|
||||
#define AUTOCOMPLETE_MATCHES_SCHEME_PENALTY (-20)
|
||||
|
||||
// Number of results we will consider for each prefix. Each prefix lookup is
|
||||
// done separately. Typically, we will only match one prefix, so this should be
|
||||
// a sufficient number to give "enough" autocomplete matches per prefix. The
|
||||
// total number of results that could ever be returned is this times the number
|
||||
// of prefixes. This should be as small as is reasonable to make it faster.
|
||||
#define AUTOCOMPLETE_MAX_PER_PREFIX 50
|
||||
|
||||
// This is the maximum results we'll return for a "typed" search (usually
|
||||
// happens in response to clicking the down arrow next to the URL).
|
||||
#define AUTOCOMPLETE_MAX_PER_TYPED 100
|
||||
|
||||
// This is the maximum number of visits that an item can have for us to
|
||||
// try to remove the path and put a virtual item with just the hostname as the
|
||||
// first entry. The virtual item helps the case where you've visited a site deep
|
||||
// down and want to visit the main site. This limit makes sure we don't take
|
||||
// the first autocomplete spot for a page you are more likely to go to.
|
||||
#define AUTOCOMPLETE_MAX_TRUNCATION_VISIT 6
|
||||
|
||||
PRInt32 ComputeAutoCompletePriority(const nsAString& aUrl, PRInt32 aVisitCount,
|
||||
PRBool aWasTyped);
|
||||
PRBool aWasTyped, PRBool aIsBookmarked);
|
||||
nsresult NormalizeAutocompleteInput(const nsAString& aInput,
|
||||
nsString& aOutput);
|
||||
|
||||
// nsIAutoCompleteSearch *******************************************************
|
||||
|
||||
|
@ -68,54 +167,140 @@ PRInt32 ComputeAutoCompletePriority(const nsAString& aUrl, PRInt32 aVisitCount,
|
|||
|
||||
struct AutoCompleteIntermediateResult
|
||||
{
|
||||
AutoCompleteIntermediateResult(const nsString& aUrl, const nsString& aTitle,
|
||||
PRInt32 aVisitCount, PRInt32 aPriority) :
|
||||
url(aUrl), title(aTitle), visitCount(aVisitCount), priority(aPriority) {}
|
||||
nsString url;
|
||||
nsString title;
|
||||
PRUint32 priority;
|
||||
PRInt32 visitCount;
|
||||
PRInt32 priority;
|
||||
};
|
||||
class AutoCompleteIntermediateResultSet
|
||||
|
||||
|
||||
// AutoCompleteResultComparator
|
||||
|
||||
class AutoCompleteResultComparator
|
||||
{
|
||||
public:
|
||||
AutoCompleteIntermediateResultSet()
|
||||
{
|
||||
AutoCompleteResultComparator(nsNavHistory* history) : mHistory(history) {}
|
||||
|
||||
PRBool Equals(const AutoCompleteIntermediateResult& a,
|
||||
const AutoCompleteIntermediateResult& b) const {
|
||||
// Don't need an equals, this call will be optimized out when it
|
||||
// is used by nsQuickSortComparator above
|
||||
return PR_FALSE;
|
||||
}
|
||||
~AutoCompleteIntermediateResultSet()
|
||||
{
|
||||
for (PRInt32 i = 0; i < mArray.Count(); i ++) {
|
||||
AutoCompleteIntermediateResult* cur = NS_STATIC_CAST(
|
||||
AutoCompleteIntermediateResult*, mArray[i]);
|
||||
delete cur;
|
||||
}
|
||||
}
|
||||
PRBool Add(const nsString& aURL, const nsString& aTitle, PRInt32 aPriority)
|
||||
{
|
||||
AutoCompleteIntermediateResult* cur = new AutoCompleteIntermediateResult;
|
||||
if (! cur)
|
||||
return PR_FALSE;
|
||||
cur->url = aURL;
|
||||
cur->title = aTitle;
|
||||
cur->priority = aPriority;
|
||||
mArray.AppendElement(cur);
|
||||
return PR_TRUE;
|
||||
}
|
||||
void Sort(nsNavHistory* navHistory)
|
||||
{
|
||||
mArray.Sort(nsNavHistory::AutoCompleteSortComparison, navHistory);
|
||||
}
|
||||
nsresult FillResult(nsIAutoCompleteSimpleResult* aResult)
|
||||
{
|
||||
for (PRInt32 i = 0; i < mArray.Count(); i ++)
|
||||
PRBool LessThan(const AutoCompleteIntermediateResult& match1,
|
||||
const AutoCompleteIntermediateResult& match2) const {
|
||||
// we actually compare GREATER than here, since we want the array to be in
|
||||
// most relevant (highest priority value) first
|
||||
|
||||
// In most cases the priorities will be different and we just use them
|
||||
if (match1.priority != match2.priority)
|
||||
{
|
||||
AutoCompleteIntermediateResult* cur = NS_STATIC_CAST(
|
||||
AutoCompleteIntermediateResult*, mArray[i]);
|
||||
nsresult rv = aResult->AppendMatch(cur->url, cur->title);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return match1.priority > match2.priority;
|
||||
}
|
||||
return NS_OK;
|
||||
else
|
||||
{
|
||||
// secondary sorting gives priority to site names and paths (ending in a /)
|
||||
PRBool isPath1 = PR_FALSE, isPath2 = PR_FALSE;
|
||||
if (!match1.url.IsEmpty())
|
||||
isPath1 = (match1.url.Last() == PRUnichar('/'));
|
||||
if (!match2.url.IsEmpty())
|
||||
isPath2 = (match2.url.Last() == PRUnichar('/'));
|
||||
|
||||
if (isPath1 && !isPath2) return PR_FALSE; // match1->url is a website/path, match2->url isn't
|
||||
if (!isPath1 && isPath2) return PR_TRUE; // match1->url isn't a website/path, match2->url is
|
||||
|
||||
// find the prefixes so we can sort by the stuff after the prefixes
|
||||
PRInt32 prefix1 = mHistory->AutoCompleteGetPrefixLength(match1.url);
|
||||
PRInt32 prefix2 = mHistory->AutoCompleteGetPrefixLength(match2.url);
|
||||
|
||||
// Compare non-prefixed urls using the current locale string compare. This will sort
|
||||
// things alphabetically (ignoring common prefixes). For example, "http://www.abc.com/"
|
||||
// will come before "ftp://ftp.xyz.com"
|
||||
PRInt32 ret = 0;
|
||||
mHistory->mCollation->CompareString(
|
||||
nsICollation::kCollationCaseInSensitive,
|
||||
Substring(match1.url, prefix1), Substring(match2.url, prefix2),
|
||||
&ret);
|
||||
if (ret != 0)
|
||||
return ret > 0;
|
||||
|
||||
// sort http://xyz.com before http://www.xyz.com
|
||||
return prefix1 > prefix2;
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
protected:
|
||||
nsVoidArray mArray;
|
||||
nsNavHistory* mHistory;
|
||||
};
|
||||
|
||||
|
||||
// nsNavHistory::InitAutoComplete
|
||||
|
||||
nsresult
|
||||
nsNavHistory::InitAutoComplete()
|
||||
{
|
||||
nsresult rv = CreateAutoCompleteQuery();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
AutoCompletePrefix* ok;
|
||||
|
||||
// These are the prefixes we check for implicitly. Prefixes with a
|
||||
// host portion (like "www.") get their second level flag set.
|
||||
ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("http://"), PR_FALSE));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("http://www."), PR_TRUE));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("ftp://"), PR_FALSE));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("ftp://ftp."), PR_TRUE));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("https://"), PR_FALSE));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("https://www."), PR_TRUE));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// nsNavHistory::CreateAutoCompleteQuery
|
||||
//
|
||||
// The auto complete query we use depends on options, so we have it in
|
||||
// a separate function so it can be re-created when the option changes.
|
||||
|
||||
nsresult
|
||||
nsNavHistory::CreateAutoCompleteQuery()
|
||||
{
|
||||
nsCString sql;
|
||||
if (mAutoCompleteOnlyTyped) {
|
||||
sql = NS_LITERAL_CSTRING(
|
||||
"SELECT url, title, visit_count, typed, "
|
||||
"(SELECT item_child FROM moz_bookmarks WHERE item_child = id) "
|
||||
"FROM moz_history "
|
||||
"WHERE url >= ?1 AND url < ?2 "
|
||||
"AND typed = 1 "
|
||||
"ORDER BY visit_count DESC "
|
||||
"LIMIT ");
|
||||
} else {
|
||||
sql = NS_LITERAL_CSTRING(
|
||||
"SELECT url, title, visit_count, typed, "
|
||||
"(SELECT item_child FROM moz_bookmarks WHERE item_child = id) "
|
||||
"FROM moz_history "
|
||||
"WHERE url >= ?1 AND url < ?2 "
|
||||
"AND (hidden <> 1 OR typed = 1) "
|
||||
"ORDER BY visit_count DESC "
|
||||
"LIMIT ");
|
||||
}
|
||||
sql.AppendInt(AUTOCOMPLETE_MAX_PER_PREFIX);
|
||||
nsresult rv = mDBConn->CreateStatement(sql,
|
||||
getter_AddRefs(mDBAutoCompleteQuery));
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
// nsNavHistory::StartSearch
|
||||
//
|
||||
|
||||
|
@ -134,33 +319,18 @@ nsNavHistory::StartSearch(const nsAString & aSearchString,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
result->SetSearchString(aSearchString);
|
||||
|
||||
PRBool usePrevious = (aPreviousResult != nsnull);
|
||||
|
||||
// throw away previous results if the search string is empty after it has had
|
||||
// the prefixes removed: if the user types "http://" throw away the search
|
||||
// for "http:/" and start fresh with the new query.
|
||||
if (usePrevious) {
|
||||
nsAutoString cut(aSearchString);
|
||||
AutoCompleteCutPrefix(&cut, nsnull);
|
||||
if (cut.IsEmpty())
|
||||
usePrevious = PR_FALSE;
|
||||
}
|
||||
|
||||
// throw away previous results if the new string doesn't begin with the
|
||||
// previous search string
|
||||
if (usePrevious) {
|
||||
nsAutoString previousSearch;
|
||||
aPreviousResult->GetSearchString(previousSearch);
|
||||
if (previousSearch.Length() > aSearchString.Length() ||
|
||||
!Substring(aSearchString, 0, previousSearch.Length()).Equals(previousSearch)) {
|
||||
usePrevious = PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Performance: We can improve performance for refinements of a previous
|
||||
// result by filtering the old result with the new string. However, since
|
||||
// our results are not a full match of history, we'll need to requery if
|
||||
// any of the subresults returned the maximum number of elements (i.e. we
|
||||
// didn't load all of them).
|
||||
//
|
||||
// Timing measurements show that the performance of this is actually very
|
||||
// good for specific queries. Thus, the times where we can do the
|
||||
// optimization (when there are few results) are exactly the times when
|
||||
// we don't have to. As a result, we keep it this much simpler way.
|
||||
if (aSearchString.IsEmpty()) {
|
||||
rv = AutoCompleteTypedSearch(result);
|
||||
} else if (usePrevious) {
|
||||
rv = AutoCompleteRefineHistorySearch(aSearchString, aPreviousResult, result);
|
||||
} else {
|
||||
rv = AutoCompleteFullHistorySearch(aSearchString, result);
|
||||
}
|
||||
|
@ -203,22 +373,38 @@ nsNavHistory::StopSearch()
|
|||
nsresult nsNavHistory::AutoCompleteTypedSearch(
|
||||
nsIAutoCompleteSimpleResult* result)
|
||||
{
|
||||
// note: need to test against hidden = 1 since the column can also be null
|
||||
// (meaning not hidden)
|
||||
// need to get more than the required minimum number since some will be dupes
|
||||
nsCOMPtr<mozIStorageStatement> dbSelectStatement;
|
||||
nsresult rv = mDBConn->CreateStatement(
|
||||
NS_LITERAL_CSTRING("SELECT url,title FROM moz_history WHERE typed = 1 AND hidden <> 1 ORDER BY visit_count DESC LIMIT 1000"),
|
||||
getter_AddRefs(dbSelectStatement));
|
||||
nsCString sql = NS_LITERAL_CSTRING(
|
||||
"SELECT url, title "
|
||||
"FROM moz_historyvisit v JOIN moz_history h ON v.page_id = h.id "
|
||||
"WHERE h.typed = 1 ORDER BY visit_count DESC LIMIT ");
|
||||
sql.AppendInt(AUTOCOMPLETE_MAX_PER_TYPED * 3);
|
||||
nsresult rv = mDBConn->CreateStatement(sql, getter_AddRefs(dbSelectStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// prevent duplicates
|
||||
nsDataHashtable<nsStringHashKey, PRInt32> urls;
|
||||
if (! urls.Init(500))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
PRInt32 dummy;
|
||||
PRInt32 count = 0;
|
||||
PRBool hasMore = PR_FALSE;
|
||||
while (NS_SUCCEEDED(dbSelectStatement->ExecuteStep(&hasMore)) && hasMore) {
|
||||
while (count < AUTOCOMPLETE_MAX_PER_TYPED &&
|
||||
NS_SUCCEEDED(dbSelectStatement->ExecuteStep(&hasMore)) && hasMore) {
|
||||
nsAutoString entryURL, entryTitle;
|
||||
dbSelectStatement->GetString(0, entryURL);
|
||||
dbSelectStatement->GetString(1, entryTitle);
|
||||
|
||||
rv = result->AppendMatch(entryURL, entryTitle);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (! urls.Get(entryURL, &dummy)) {
|
||||
// new item
|
||||
rv = result->AppendMatch(entryURL, entryTitle);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
urls.Put(entryURL, 1);
|
||||
count ++;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -234,78 +420,116 @@ nsresult nsNavHistory::AutoCompleteTypedSearch(
|
|||
|
||||
nsresult
|
||||
nsNavHistory::AutoCompleteFullHistorySearch(const nsAString& aSearchString,
|
||||
nsIAutoCompleteSimpleResult* result)
|
||||
nsIAutoCompleteSimpleResult* aResult)
|
||||
{
|
||||
// figure out whether any known prefixes are present in the search string
|
||||
// so we know not to ignore them when comparing results later
|
||||
AutoCompleteExclude exclude;
|
||||
AutoCompleteGetExcludeInfo(aSearchString, &exclude);
|
||||
nsString searchString;
|
||||
nsresult rv = NormalizeAutocompleteInput(aSearchString, searchString);
|
||||
if (NS_FAILED(rv))
|
||||
return NS_OK; // no matches for invalid input
|
||||
|
||||
mozStorageStatementScoper scoper(mDBFullAutoComplete);
|
||||
nsresult rv = mDBFullAutoComplete->BindInt32Parameter(0, mAutoCompleteMaxCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsTArray<AutoCompleteIntermediateResult> matches;
|
||||
|
||||
// load the intermediate result so it can be sorted
|
||||
AutoCompleteIntermediateResultSet resultSet;
|
||||
PRBool hasMore = PR_FALSE;
|
||||
while (NS_SUCCEEDED(mDBFullAutoComplete->ExecuteStep(&hasMore)) && hasMore) {
|
||||
|
||||
PRBool typed = mDBFullAutoComplete->AsInt32(kAutoCompleteIndex_Typed);
|
||||
if (!typed) {
|
||||
if (mAutoCompleteOnlyTyped)
|
||||
continue;
|
||||
// Try a query using this search string and every prefix. Keep track of
|
||||
// known prefixes that the input matches for exclusion later
|
||||
PRUint32 i;
|
||||
const nsTArray<PRInt32> emptyArray;
|
||||
nsTArray<PRInt32> firstLevelMatches;
|
||||
nsTArray<PRInt32> secondLevelMatches;
|
||||
for (i = 0; i < mAutoCompletePrefixes.Length(); i ++) {
|
||||
if (StringBeginsWith(mAutoCompletePrefixes[i].prefix, searchString)) {
|
||||
if (mAutoCompletePrefixes[i].secondLevel)
|
||||
secondLevelMatches.AppendElement(i);
|
||||
else
|
||||
firstLevelMatches.AppendElement(i);
|
||||
}
|
||||
|
||||
nsAutoString entryurl;
|
||||
mDBFullAutoComplete->GetString(kAutoCompleteIndex_URL, entryurl);
|
||||
if (AutoCompleteCompare(entryurl, aSearchString, exclude)) {
|
||||
nsAutoString entrytitle;
|
||||
rv = mDBFullAutoComplete->GetString(kAutoCompleteIndex_Title, entrytitle);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
PRInt32 priority = ComputeAutoCompletePriority(entryurl,
|
||||
mDBFullAutoComplete->AsInt32(kAutoCompleteIndex_VisitCount),
|
||||
typed);
|
||||
// current string to search for
|
||||
nsString cur = mAutoCompletePrefixes[i].prefix + searchString;
|
||||
|
||||
if (! resultSet.Add(entryurl, entrytitle, priority))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
// see if the concatenated string itself matches any prefixes
|
||||
nsTArray<PRInt32> curPrefixMatches;
|
||||
for (PRUint32 prefix = 0; prefix < mAutoCompletePrefixes.Length(); prefix ++) {
|
||||
if (StringBeginsWith(mAutoCompletePrefixes[prefix].prefix, cur))
|
||||
curPrefixMatches.AppendElement(prefix);
|
||||
}
|
||||
|
||||
// search for the current string, excluding those matching prefixes
|
||||
AutoCompleteQueryOnePrefix(cur, curPrefixMatches, 0, &matches);
|
||||
|
||||
// search for each of those matching prefixes, applying the prefix penalty
|
||||
for (PRUint32 match = 0; match < curPrefixMatches.Length(); match ++) {
|
||||
AutoCompleteQueryOnePrefix(mAutoCompletePrefixes[curPrefixMatches[match]].prefix,
|
||||
emptyArray, AUTOCOMPLETE_MATCHES_PREFIX_PENALTY,
|
||||
&matches);
|
||||
}
|
||||
}
|
||||
|
||||
// sort and populate the final result
|
||||
resultSet.Sort(this);
|
||||
return resultSet.FillResult(result);
|
||||
}
|
||||
// Now try searching with no prefix
|
||||
if (firstLevelMatches.Length() > 0) {
|
||||
// This will get all matches that DON'T match any prefix. For example, if
|
||||
// the user types "http://w" we will match "http://westinghouse.com" but
|
||||
// not "http://www.something".
|
||||
AutoCompleteQueryOnePrefix(searchString,
|
||||
firstLevelMatches, 0, &matches);
|
||||
} else if (secondLevelMatches.Length() > 0) {
|
||||
// if there are no first level matches (i.e. "http://") then we fall back on
|
||||
// second level matches. Here, we assume that a first level match implies
|
||||
// a second level match as well, so we only have to check when there are no
|
||||
// first level matches.
|
||||
AutoCompleteQueryOnePrefix(searchString,
|
||||
secondLevelMatches, 0, &matches);
|
||||
|
||||
// now we try to fill in matches of the prefix. For example, if you type
|
||||
// "http://w" we will still match against "http://www." but with a penalty.
|
||||
// We only do this for second level prefixes.
|
||||
for (PRUint32 match = 0; match < secondLevelMatches.Length(); match ++) {
|
||||
AutoCompleteQueryOnePrefix(mAutoCompletePrefixes[secondLevelMatches[match]].prefix,
|
||||
emptyArray, AUTOCOMPLETE_MATCHES_SCHEME_PENALTY,
|
||||
&matches);
|
||||
}
|
||||
} else {
|
||||
// Input matched no prefix, try to query for all URLs beinning with this
|
||||
// exact input.
|
||||
AutoCompleteQueryOnePrefix(searchString, emptyArray,
|
||||
AUTOCOMPLETE_MATCHES_SCHEME_PENALTY, &matches);
|
||||
}
|
||||
|
||||
// nsNavHistory::AutoCompleteRefineHistorySearch
|
||||
//
|
||||
// Called when a previous autocomplete result exists and we are adding to
|
||||
// the query (making it more specific). The list is already nicely sorted,
|
||||
// so we can just remove all the old elements that DON'T match the new query.
|
||||
// sort according to priorities
|
||||
AutoCompleteResultComparator comparator(this);
|
||||
matches.Sort(comparator);
|
||||
|
||||
nsresult
|
||||
nsNavHistory::AutoCompleteRefineHistorySearch(
|
||||
const nsAString& aSearchString,
|
||||
nsIAutoCompleteResult* aPreviousResult,
|
||||
nsIAutoCompleteSimpleResult* aNewResult)
|
||||
{
|
||||
// figure out whether any known prefixes are present in the search string
|
||||
// so we know not to ignore them when comparing results later
|
||||
AutoCompleteExclude exclude;
|
||||
AutoCompleteGetExcludeInfo(aSearchString, &exclude);
|
||||
|
||||
PRUint32 matchCount;
|
||||
aPreviousResult->GetMatchCount(&matchCount);
|
||||
|
||||
for (PRUint32 i = 0; i < matchCount; i ++) {
|
||||
nsAutoString cururl;
|
||||
nsresult rv = aPreviousResult->GetValueAt(i, cururl);
|
||||
// fill into result
|
||||
nsAutoString zerothEntry;
|
||||
if (matches.Length() > 0 &&
|
||||
matches[0].visitCount <= AUTOCOMPLETE_MAX_TRUNCATION_VISIT) {
|
||||
// Here, we try to make sure that the first match is always a host name
|
||||
// we take the previous first match and extract its host name and add it
|
||||
// before. If the first item has been visited a lot, don't do that because
|
||||
// they probably want to go there instead
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(matches[0].url));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (AutoCompleteCompare(cururl, aSearchString, exclude)) {
|
||||
nsAutoString curcomment;
|
||||
rv = aPreviousResult->GetCommentAt(i, curcomment);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = aNewResult->AppendMatch(cururl, curcomment);
|
||||
uri->SetPath(NS_LITERAL_CSTRING("/"));
|
||||
|
||||
nsCAutoString spec;
|
||||
uri->GetSpec(spec);
|
||||
zerothEntry = NS_ConvertUTF8toUTF16(spec);
|
||||
|
||||
if (! zerothEntry.Equals(matches[0].url))
|
||||
aResult->AppendMatch(zerothEntry, EmptyString());
|
||||
rv = aResult->AppendMatch(matches[0].url, matches[0].title);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (matches.Length() > 0) {
|
||||
// otherwise, just append the first match
|
||||
rv = aResult->AppendMatch(matches[0].url, matches[0].title);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
for (i = 1; i < matches.Length(); i ++) {
|
||||
// only add ones that are NOT the same as the previous one. It's possible
|
||||
// to get duplicates from the queries.
|
||||
if (! matches[i].url.Equals(matches[i-1].url) &&
|
||||
! zerothEntry.Equals(matches[i].url)) {
|
||||
rv = aResult->AppendMatch(matches[i].url, matches[i].title);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
@ -313,162 +537,89 @@ nsNavHistory::AutoCompleteRefineHistorySearch(
|
|||
}
|
||||
|
||||
|
||||
// nsNavHistory::AutoCompleteGetExcludeInfo
|
||||
// nsNavHistory::AutoCompleteQueryOnePrefix
|
||||
//
|
||||
// If aURL begins with a protocol or domain prefix from our lists, then mark
|
||||
// their index in an AutocompleteExclude struct.
|
||||
// The values in aExcludePrefixes are indices into mAutoCompletePrefixes
|
||||
// of prefixes to exclude during this query. For example, if I type
|
||||
// "ht" this function will be called to match everything starting with
|
||||
// "ht" EXCEPT "http://" and "https://".
|
||||
|
||||
void
|
||||
nsNavHistory::AutoCompleteGetExcludeInfo(const nsAString& aURL,
|
||||
AutoCompleteExclude* aExclude)
|
||||
nsresult
|
||||
nsNavHistory::AutoCompleteQueryOnePrefix(const nsString& aSearchString,
|
||||
const nsTArray<PRInt32>& aExcludePrefixes,
|
||||
PRInt32 aPriorityDelta,
|
||||
nsTArray<AutoCompleteIntermediateResult>* aResult)
|
||||
{
|
||||
aExclude->schemePrefix = -1;
|
||||
aExclude->hostnamePrefix = -1;
|
||||
// All URL queries are in UTF-8. Compute the beginning (inclusive) and
|
||||
// ending (exclusive) of the range of URLs to include when compared
|
||||
// using strcmp (which is what sqlite does).
|
||||
nsCAutoString beginQuery = NS_ConvertUTF16toUTF8(aSearchString);
|
||||
if (beginQuery.IsEmpty())
|
||||
return NS_OK;
|
||||
nsCAutoString endQuery = beginQuery;
|
||||
unsigned char maxChar[6] = { 0xfd, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf };
|
||||
endQuery.Append(NS_REINTERPRET_CAST(const char*, maxChar), 6);
|
||||
|
||||
PRInt32 index = 0;
|
||||
PRInt32 i;
|
||||
for (i = 0; i < mIgnoreSchemes.Count(); ++i) {
|
||||
nsString* string = mIgnoreSchemes.StringAt(i);
|
||||
if (Substring(aURL, 0, string->Length()).Equals(*string)) {
|
||||
aExclude->schemePrefix = i;
|
||||
index = string->Length();
|
||||
break;
|
||||
nsTArray<nsCString> ranges;
|
||||
if (aExcludePrefixes.Length() > 0) {
|
||||
// we've been requested to include holes in our range, sort these ranges
|
||||
ranges.AppendElement(beginQuery);
|
||||
for (PRUint32 i = 0; i < aExcludePrefixes.Length(); i ++) {
|
||||
nsCAutoString thisPrefix = NS_ConvertUTF16toUTF8(
|
||||
mAutoCompletePrefixes[aExcludePrefixes[i]].prefix);
|
||||
ranges.AppendElement(thisPrefix);
|
||||
thisPrefix.Append(NS_REINTERPRET_CAST(const char*, maxChar), 6);
|
||||
ranges.AppendElement(thisPrefix);
|
||||
}
|
||||
ranges.AppendElement(endQuery);
|
||||
ranges.Sort();
|
||||
} else {
|
||||
// simple range with no holes
|
||||
ranges.AppendElement(beginQuery);
|
||||
ranges.AppendElement(endQuery);
|
||||
}
|
||||
|
||||
for (i = 0; i < mIgnoreHostnames.Count(); ++i) {
|
||||
nsString* string = mIgnoreHostnames.StringAt(i);
|
||||
if (Substring(aURL, index, string->Length()).Equals(*string)) {
|
||||
aExclude->hostnamePrefix = i;
|
||||
index += string->Length();
|
||||
break;
|
||||
NS_ASSERTION(ranges.Length() % 2 == 0, "Ranges should be pairs!");
|
||||
|
||||
// The nested select expands to nonzero when the item is bookmarked.
|
||||
// It might be nice to also select hidden bookmarks (unvisited) but that
|
||||
// made this statement more complicated and should be an unusual case.
|
||||
nsresult rv;
|
||||
for (PRUint32 range = 0; range < ranges.Length() - 1; range += 2) {
|
||||
mozStorageStatementScoper scoper(mDBAutoCompleteQuery);
|
||||
|
||||
rv = mDBAutoCompleteQuery->BindUTF8StringParameter(0, ranges[range]);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mDBAutoCompleteQuery->BindUTF8StringParameter(1, ranges[range + 1]);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool hasMore;
|
||||
nsAutoString url, title;
|
||||
while (NS_SUCCEEDED(mDBAutoCompleteQuery->ExecuteStep(&hasMore)) && hasMore) {
|
||||
mDBAutoCompleteQuery->GetString(0, url);
|
||||
mDBAutoCompleteQuery->GetString(1, title);
|
||||
PRInt32 visitCount = mDBAutoCompleteQuery->AsInt32(2);
|
||||
PRInt32 priority = ComputeAutoCompletePriority(url, visitCount,
|
||||
mDBAutoCompleteQuery->AsInt32(3) > 0,
|
||||
mDBAutoCompleteQuery->AsInt32(4) > 0) + aPriorityDelta;
|
||||
aResult->AppendElement(AutoCompleteIntermediateResult(
|
||||
url, title, visitCount, priority));
|
||||
}
|
||||
}
|
||||
|
||||
aExclude->postPrefixOffset = index;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// nsNavHistory::AutoCompleteCutPrefix
|
||||
//
|
||||
// Cut any protocol and domain prefixes from aURL, except for those which
|
||||
// are specified in aExclude.
|
||||
//
|
||||
// aExclude can be NULL to cut all possible prefixes
|
||||
//
|
||||
// This comparison is case-sensitive. Therefore, it assumes that aUserURL
|
||||
// is a potential URL whose host name is in all lower case.
|
||||
//
|
||||
// The aExclude identifies which prefixes were present in the user's typed
|
||||
// entry, which we DON'T want to cut so that it can match the user input.
|
||||
// For example. Typed "xyz.com" matches "xyz.com" AND "http://www.xyz.com"
|
||||
// but "www.xyz.com" still matches "www.xyz.com".
|
||||
// nsNavHistory::AutoCompleteGetPrefixLength
|
||||
|
||||
void
|
||||
nsNavHistory::AutoCompleteCutPrefix(nsString* aURL,
|
||||
const AutoCompleteExclude* aExclude)
|
||||
PRInt32
|
||||
nsNavHistory::AutoCompleteGetPrefixLength(const nsString& aSpec)
|
||||
{
|
||||
PRInt32 idx = 0;
|
||||
PRInt32 i;
|
||||
for (i = 0; i < mIgnoreSchemes.Count(); ++i) {
|
||||
if (aExclude && i == aExclude->schemePrefix)
|
||||
continue;
|
||||
nsString* string = mIgnoreSchemes.StringAt(i);
|
||||
if (Substring(*aURL, 0, string->Length()).Equals(*string)) {
|
||||
idx = string->Length();
|
||||
break;
|
||||
}
|
||||
for (PRUint32 i = 0; i < mAutoCompletePrefixes.Length(); ++i) {
|
||||
if (StringBeginsWith(aSpec, mAutoCompletePrefixes[i].prefix))
|
||||
return mAutoCompletePrefixes[i].prefix.Length();
|
||||
}
|
||||
|
||||
if (idx > 0)
|
||||
aURL->Cut(0, idx);
|
||||
|
||||
idx = 0;
|
||||
for (i = 0; i < mIgnoreHostnames.Count(); ++i) {
|
||||
if (aExclude && i == aExclude->hostnamePrefix)
|
||||
continue;
|
||||
nsString* string = mIgnoreHostnames.StringAt(i);
|
||||
if (Substring(*aURL, 0, string->Length()).Equals(*string)) {
|
||||
idx = string->Length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx > 0)
|
||||
aURL->Cut(0, idx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// nsNavHistory::AutoCompleteCompare
|
||||
//
|
||||
// Determines if a given URL from the history matches the user input.
|
||||
// See AutoCompleteCutPrefix for the meaning of aExclude.
|
||||
|
||||
PRBool
|
||||
nsNavHistory::AutoCompleteCompare(const nsAString& aHistoryURL,
|
||||
const nsAString& aUserURL,
|
||||
const AutoCompleteExclude& aExclude)
|
||||
{
|
||||
nsAutoString cutHistoryURL(aHistoryURL);
|
||||
AutoCompleteCutPrefix(&cutHistoryURL, &aExclude);
|
||||
return StringBeginsWith(cutHistoryURL, aUserURL);
|
||||
}
|
||||
|
||||
|
||||
// nsGlobalHistory::AutoCompleteSortComparison
|
||||
//
|
||||
// The design and reasoning behind the following autocomplete sort
|
||||
// implementation is documented in bug 78270. In most cases, the precomputed
|
||||
// priorities (by ComputeAutoCompletePriority) are used without further
|
||||
// computation.
|
||||
|
||||
PRInt32 PR_CALLBACK // static
|
||||
nsNavHistory::AutoCompleteSortComparison(const void* match1Void,
|
||||
const void* match2Void,
|
||||
void *navHistoryVoid)
|
||||
{
|
||||
// get our class pointer (this is a static function)
|
||||
nsNavHistory* navHistory = NS_STATIC_CAST(nsNavHistory*, navHistoryVoid);
|
||||
|
||||
const AutoCompleteIntermediateResult* match1 = NS_STATIC_CAST(
|
||||
const AutoCompleteIntermediateResult*, match1Void);
|
||||
const AutoCompleteIntermediateResult* match2 = NS_STATIC_CAST(
|
||||
const AutoCompleteIntermediateResult*, match2Void);
|
||||
|
||||
// In most cases the priorities will be different and we just use them
|
||||
if (match1->priority != match2->priority)
|
||||
{
|
||||
return match2->priority - match1->priority;
|
||||
}
|
||||
else
|
||||
{
|
||||
// secondary sorting gives priority to site names and paths (ending in a /)
|
||||
PRBool isPath1 = PR_FALSE, isPath2 = PR_FALSE;
|
||||
if (!match1->url.IsEmpty())
|
||||
isPath1 = (match1->url.Last() == PRUnichar('/'));
|
||||
if (!match2->url.IsEmpty())
|
||||
isPath2 = (match2->url.Last() == PRUnichar('/'));
|
||||
|
||||
if (isPath1 && !isPath2) return -1; // match1->url is a website/path, match2->url isn't
|
||||
if (!isPath1 && isPath2) return 1; // match1->url isn't a website/path, match2->url is
|
||||
|
||||
// find the prefixes so we can sort by the stuff after the prefixes
|
||||
AutoCompleteExclude prefix1, prefix2;
|
||||
navHistory->AutoCompleteGetExcludeInfo(match1->url, &prefix1);
|
||||
navHistory->AutoCompleteGetExcludeInfo(match2->url, &prefix2);
|
||||
|
||||
// compare non-prefixed urls
|
||||
PRInt32 ret = Compare(
|
||||
Substring(match1->url, prefix1.postPrefixOffset, match1->url.Length()),
|
||||
Substring(match2->url, prefix2.postPrefixOffset, match2->url.Length()));
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
// sort http://xyz.com before http://www.xyz.com
|
||||
return prefix1.postPrefixOffset - prefix2.postPrefixOffset;
|
||||
}
|
||||
return 0;
|
||||
return 0; // no prefix
|
||||
}
|
||||
|
||||
|
||||
|
@ -489,7 +640,7 @@ nsNavHistory::AutoCompleteSortComparison(const void* match1Void,
|
|||
|
||||
PRInt32
|
||||
ComputeAutoCompletePriority(const nsAString& aUrl, PRInt32 aVisitCount,
|
||||
PRBool aWasTyped)
|
||||
PRBool aWasTyped, PRBool aIsBookmarked)
|
||||
{
|
||||
PRInt32 aPriority = aVisitCount;
|
||||
|
||||
|
@ -500,7 +651,61 @@ ComputeAutoCompletePriority(const nsAString& aUrl, PRInt32 aVisitCount,
|
|||
}
|
||||
|
||||
if (aWasTyped)
|
||||
aPriority += AUTOCOMPLETE_NONPAGE_VISIT_COUNT_BOOST;
|
||||
aPriority += AUTOCOMPLETE_TYPED_BOOST;
|
||||
if (aIsBookmarked)
|
||||
aPriority += AUTOCOMPLETE_BOOKMARKED_BOOST;
|
||||
|
||||
return aPriority;
|
||||
}
|
||||
|
||||
|
||||
// NormalizeAutocompleteInput
|
||||
|
||||
nsresult NormalizeAutocompleteInput(const nsAString& aInput,
|
||||
nsString& aOutput)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (aInput.IsEmpty()) {
|
||||
aOutput.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
nsCAutoString input = NS_ConvertUTF16toUTF8(aInput);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), input);
|
||||
PRBool isSchemeAdded = PR_FALSE;
|
||||
if (NS_FAILED(rv)) {
|
||||
// it may have failed because there is no scheme, prepend one
|
||||
isSchemeAdded = PR_TRUE;
|
||||
input = NS_LITERAL_CSTRING("http://") + input;
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(uri), input);
|
||||
if (NS_FAILED(rv))
|
||||
return rv; // still not valid, can't autocomplete this URL
|
||||
}
|
||||
|
||||
nsCAutoString spec;
|
||||
rv = uri->GetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (spec.IsEmpty())
|
||||
return NS_OK; // should never happen but we assume it's not empty below, so check
|
||||
|
||||
aOutput = NS_ConvertUTF8toUTF16(spec);
|
||||
|
||||
// trim the "http://" scheme if we added it
|
||||
if (isSchemeAdded) {
|
||||
NS_ASSERTION(aOutput.Length() > 7, "String impossibly short");
|
||||
aOutput = Substring(aOutput, 7);
|
||||
}
|
||||
|
||||
// it may have appended a slash, get rid of it
|
||||
// example: input was "http://www.mozil" the URI spec will be
|
||||
// "http://www.mozil/" which is obviously wrong to complete against.
|
||||
// However, it won't always append a slash, for example, for the input
|
||||
// "http://www.mozilla.org/supp"
|
||||
if (input[input.Length() - 1] != '/' && aOutput[aOutput.Length() - 1] == '/')
|
||||
aOutput.Truncate(aOutput.Length() - 1);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче