Bug 497722 Address auto-complete gives random order, not based on frequency after upgrading from Thunderbird 2 to Thunderbird 3 (autocomplete). r/sr=bienvenu

This commit is contained in:
Mark Banner 2010-02-04 15:14:57 +00:00
Родитель 2ba9d592d9
Коммит ed30efc057
5 изменённых файлов: 366 добавлений и 14 удалений

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

@ -115,6 +115,41 @@ nsAbAutoCompleteSearch.prototype = {
// Private methods
/**
* Returns the popularity index for a given card. This takes account of a
* translation bug whereby Thunderbird 2 stores its values in mork as
* hexadecimal, and Thunderbird 3 stores as decimal.
*
* @param aDirectory The directory that the card is in.
* @param aCard The card to return the popularity index for.
*/
_getPopularityIndex: function _getPopularityIndex(aDirectory, aCard) {
let popularityValue = aCard.getProperty("PopularityIndex", "0");
let popularityIndex = parseInt(popularityValue);
// If we haven't parsed it the first time round, parse it as hexadecimal
// and repair so that we don't have to keep repairing.
if (isNaN(popularityIndex)) {
popularityIndex = parseInt(popularityValue, 16);
// If its still NaN, just give up, we shouldn't ever get here.
if (isNaN(popularityIndex))
popularityIndex = 0;
// Now store this change so that we're not changing it each time around.
if (!aDirectory.readOnly) {
aCard.setProperty("PopularityIndex", popularityIndex);
try {
aDirectory.modifyCard(aCard);
}
catch (ex) {
Components.utils.reportError(e);
}
}
}
return popularityIndex;
},
/**
* Searches cards in the given directory. It is not expected to search against
* email addresses (use _searchWithinEmails). If a card is matched (and isn't
@ -138,15 +173,15 @@ nsAbAutoCompleteSearch.prototype = {
if (card instanceof Components.interfaces.nsIAbCard) {
if (card.isMailList)
this._addToResult(commentColumn, card, "", result);
this._addToResult(commentColumn, directory, card, "", result);
else {
let email = card.primaryEmail;
if (email)
this._addToResult(commentColumn, card, email, result);
this._addToResult(commentColumn, directory, card, email, result);
email = card.getProperty("SecondEmail", "");
if (email)
this._addToResult(commentColumn, card, email, result);
this._addToResult(commentColumn, directory, card, email, result);
}
}
}
@ -177,17 +212,17 @@ nsAbAutoCompleteSearch.prototype = {
if (card instanceof Components.interfaces.nsIAbCard) {
if (card.isMailList)
this._addToResult(commentColumn, card, "", result);
this._addToResult(commentColumn, directory, card, "", result);
else {
let email = card.primaryEmail;
if (email && email.toLocaleLowerCase()
.lastIndexOf(fullString, 0) == 0)
this._addToResult(commentColumn, card, email, result);
this._addToResult(commentColumn, directory, card, email, result);
email = card.getProperty("SecondEmail", "");
if (email && email.toLocaleLowerCase()
.lastIndexOf(fullString, 0) == 0)
this._addToResult(commentColumn, card, email, result);
this._addToResult(commentColumn, directory, card, email, result);
}
}
}
@ -241,15 +276,17 @@ nsAbAutoCompleteSearch.prototype = {
* will remove the existing element if the popularity of the new card is
* higher than the previous card.
*
* @param directory The directory that the card is in.
* @param card The card that could be a duplicate.
* @param emailAddress The emailAddress (name/address combination) to check
* for duplicates against.
* @param currentResults The current results list.
*/
_checkDuplicate: function _checkDuplicate(card, emailAddress, currentResults) {
_checkDuplicate: function _checkDuplicate(directory, card, emailAddress,
currentResults) {
var lcEmailAddress = emailAddress.toLocaleLowerCase();
var popIndex = parseInt(card.getProperty("PopularityIndex", "0"));
var popIndex = this._getPopularityIndex(directory, card);
for (var i = 0; i < currentResults._searchResults.length; ++i) {
if (currentResults._searchResults[i].value.toLocaleLowerCase() ==
lcEmailAddress)
@ -275,12 +312,14 @@ nsAbAutoCompleteSearch.prototype = {
*
* @param commentColumn The text to be displayed in the comment column
* (if any).
* @param directory The directory that the card is in.
* @param card The card being added to the results.
* @param emailToUse The email address from the card that should be used
* for this result.
* @param result The result to add the new entry to.
*/
_addToResult: function _addToResult(commentColumn, card, emailToUse, result) {
_addToResult: function _addToResult(commentColumn, directory, card,
emailToUse, result) {
var emailAddress =
this._parser.makeFullAddress(card.displayName,
card.isMailList ?
@ -295,13 +334,12 @@ nsAbAutoCompleteSearch.prototype = {
// If it is a duplicate, then just return and don't add it. The
// _checkDuplicate function deals with it all for us.
if (this._checkDuplicate(card, emailAddress, result))
if (this._checkDuplicate(directory, card, emailAddress, result))
return;
// Find out where to insert the card.
var insertPosition = 0;
// Hack - mork adds in as a string, but we want to get as an integer...
var cardPopularityIndex = parseInt(card.getProperty("PopularityIndex", "0"));
var cardPopularityIndex = this._getPopularityIndex(directory, card);
while (insertPosition < result._searchResults.length &&
cardPopularityIndex <

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

@ -0,0 +1,100 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* This suite ensures that we can correctly read and re-set the popularity
* indexes on a
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const ACR = Components.interfaces.nsIAutoCompleteResult;
const results = [ { email: "d <ema@test.invalid>", dirName: kPABData.dirName },
{ email: "di <emai@test.invalid>", dirName: kPABData.dirName },
{ email: "dis <email@test.invalid>", dirName: kPABData.dirName },
{ email: "disp <e@test.invalid>", dirName: kPABData.dirName },
{ email: "displ <em@test.invalid>", dirName: kPABData.dirName },
{ email: "t <list>", dirName: kPABData.dirName },
{ email: "te <lis>", dirName: kPABData.dirName },
{ email: "tes <li>", dirName: kPABData.dirName },
{ email: "test <l>", dirName: kPABData.dirName } ];
const firstNames = [ { search: "f", expected: [4, 0, 1, 2, 3] },
{ search: "fi", expected: [4, 0, 1, 3] },
{ search: "fir", expected: [4, 0, 1] },
{ search: "firs", expected: [0, 1] },
{ search: "first", expected: [1] } ];
const lastNames = [ { search: "l", expected: [4, 0, 1, 2, 3, 5, 6, 7, 8] },
{ search: "la", expected: [4, 0, 2, 3] },
{ search: "las", expected: [4, 0, 3] },
{ search: "last", expected: [4, 0] },
{ search: "lastn", expected: [0] } ];
const inputs = [ firstNames, lastNames];
function acObserver() {}
acObserver.prototype = {
_search: null,
_result: null,
onSearchResult: function (aSearch, aResult) {
this._search = aSearch;
this._result = aResult;
}
};
function run_test() {
// Copy the data files into place
let testAB = do_get_file("../../mailnews/data/tb2hexpopularity.mab");
testAB.copyTo(gProfileDir, kPABData.fileName);
// Test - Create a new search component
let acs = Components.classes["@mozilla.org/autocomplete/search;1?name=addrbook"]
.getService(Components.interfaces.nsIAutoCompleteSearch);
let obs = new acObserver();
// Ensure we've got the comment column set up for extra checking.
let prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefSvc.setIntPref("mail.autoComplete.commentColumn", 1);
// Test - Matches
// Now check multiple matches
function checkInputItem(element, index, array) {
print("Checking " + element.search);
acs.startSearch(element.search, null, null, obs);
do_check_eq(obs._search, acs);
do_check_eq(obs._result.searchString, element.search);
do_check_eq(obs._result.searchResult, ACR.RESULT_SUCCESS);
do_check_eq(obs._result.errorDescription, null);
do_check_eq(obs._result.matchCount, element.expected.length);
do_check_eq(obs._result.defaultIndex, 0);
for (let i = 0; i < element.expected.length; ++i) {
do_check_eq(obs._result.getValueAt(i), results[element.expected[i]].email);
do_check_eq(obs._result.getCommentAt(i), results[element.expected[i]].dirName);
do_check_eq(obs._result.getStyleAt(i), "local-abook");
do_check_eq(obs._result.getImageAt(i), "");
// Card at result number 4 is the one with the TB 2 popularity set as "a"
// in the file, so check that we're now setting the popularity to 10
// and hence future tests don't have to convert it.
if (element.expected[i] == 4) {
let result = obs._result.QueryInterface(Ci.nsIAbAutoCompleteResult);
do_check_eq(result.getCardAt(i).getProperty("PopularityIndex", -1), 10);
}
}
}
function checkInputSet(element, index, array) {
element.forEach(checkInputItem);
}
inputs.forEach(checkInputSet);
};

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

@ -4790,9 +4790,33 @@ nsMsgCompose::CheckAndPopulateRecipients(PRBool aPopulateMailList,
// bump the popularity index for this card since we are about to send e-mail to it
PRUint32 popularityIndex = 0;
if (!readOnly && NS_SUCCEEDED(existingCard->GetPropertyAsUint32(
kPopularityIndexProperty, &popularityIndex)))
if (!readOnly)
{
if (NS_FAILED(existingCard->GetPropertyAsUint32(
kPopularityIndexProperty, &popularityIndex)))
{
// TB 2 wrote the popularity value as hex, so if we get here,
// then we've probably got a hex value. We'll convert it back
// to decimal, as that's the best we can do.
nsCString hexPopularity;
if (NS_SUCCEEDED(existingCard->GetPropertyAsAUTF8String(kPopularityIndexProperty, hexPopularity)))
{
#ifdef MOZILLA_INTERNAL_API
PRInt32 errorCode = 0;
#else
nsresult errorCode = NS_OK;
#endif
popularityIndex = hexPopularity.ToInteger(&errorCode, 16);
if (errorCode)
// We failed, just set it to zero.
popularityIndex = 0;
}
else
// We couldn't get it as a string either, so just reset to
// zero.
popularityIndex = 0;
}
existingCard->SetPropertyAsUint32(kPopularityIndexProperty,
++popularityIndex);

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

@ -0,0 +1,98 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Test suite for increasing the popularity of contacts via
* checkAndPopulateRecipients.
*/
// We need the address book details for this test
load("../../mailnews/resources/abSetup.js");
const MsgComposeContractID = "@mozilla.org/messengercompose/compose;1";
const MsgComposeParamsContractID = "@mozilla.org/messengercompose/composeparams;1";
const MsgComposeFieldsContractID = "@mozilla.org/messengercompose/composefields;1";
const TESTS = [
{
email: "em@test.invalid",
// TB 2 stored popularity as hex, so we need to check correct handling.
prePopularity: "a",
postPopularity: "11"
},
{
email: "e@test.invalid",
prePopularity: "0",
postPopularity: "1"
},
{
email: "e@test.invalid",
prePopularity: "1",
postPopularity: "2"
},
{
email: "em@test.invalid",
prePopularity: "11",
postPopularity: "12"
}
];
function checkPopulate(aTo, aNonHTMLRecipients, aPreferMailOut, aCheckTo)
{
let msgCompose = Cc[MsgComposeContractID].createInstance(Ci.nsIMsgCompose);
// Set up some basic fields for compose.
let fields = Cc[MsgComposeFieldsContractID].createInstance(Ci.nsIMsgCompFields);
fields.to = aTo;
// Set up some params
let params = Cc[MsgComposeParamsContractID]
.createInstance(Ci.nsIMsgComposeParams);
params.composeFields = fields;
msgCompose.Initialize(null, params);
let nonHTMLRecipients = new Object();
do_check_eq(msgCompose.checkAndPopulateRecipients(true, true,
nonHTMLRecipients),
aPreferMailOut);
do_check_eq(fields.to, aCheckTo);
do_check_eq(nonHTMLRecipients.value, aNonHTMLRecipients);
}
function run_test() {
// Test setup - copy the data files into place
let testAB = do_get_file("../../mailnews/data/tb2hexpopularity.mab");
// Copy the file to the profile directory for a PAB
testAB.copyTo(gProfileDir, kPABData.fileName);
// Check the popularity index on a couple of cards.
let abManager = Cc["@mozilla.org/abmanager;1"].getService(Ci.nsIAbManager);
let AB = abManager.getDirectory(kPABData.URI);
for (let i = 0; i < TESTS.length; ++i) {
let card = AB.cardForEmailAddress(TESTS[i].email);
do_check_true(!!card);
// Thunderbird 2 stored its popularityIndexes as hex, hence when we read it
// now we're going to get a hex value. The AB has a value of "a".
do_check_eq(card.getProperty("PopularityIndex", -1), TESTS[i].prePopularity);
// Call the check populate function.
checkPopulate(TESTS[i].email, TESTS[i].email,
Ci.nsIAbPreferMailFormat.unknown, TESTS[i].email);
// Now we've run check populate, check the popularityIndex has increased.
card = AB.cardForEmailAddress(TESTS[i].email);
do_check_true(!!card);
// Thunderbird 2 stored its popularityIndexes as hex, hence when we read it
// now we're going to get a hex value. The AB has a value of "a".
do_check_eq(card.getProperty("PopularityIndex", -1), TESTS[i].postPopularity);
}
};

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

@ -0,0 +1,92 @@
// <!-- <mdb:mork:z v="1.4"/> -->
< <(a=c)> // (f=iso-8859-1)
(B8=Custom2)(B9=Custom3)(BA=Custom4)(BB=Notes)(BC=LastModifiedDate)
(BD=RecordKey)(BE=AddrCharSet)(BF=LastRecordKey)
(C0=ns:addrbk:db:table:kind:pab)(C1=ListName)(C2=ListNickName)
(C3=ListDescription)(C4=ListTotalAddresses)(C5=LowercaseListName)
(C6=ns:addrbk:db:table:kind:deleted)
(80=ns:addrbk:db:row:scope:card:all)
(81=ns:addrbk:db:row:scope:list:all)
(82=ns:addrbk:db:row:scope:data:all)(83=FirstName)(84=LastName)
(85=PhoneticFirstName)(86=PhoneticLastName)(87=DisplayName)
(88=NickName)(89=PrimaryEmail)(8A=LowercasePrimaryEmail)
(8B=SecondEmail)(8C=DefaultEmail)(8D=CardType)(8E=PreferMailFormat)
(8F=PopularityIndex)(90=AllowRemoteContent)(91=WorkPhone)(92=HomePhone)
(93=FaxNumber)(94=PagerNumber)(95=CellularNumber)(96=WorkPhoneType)
(97=HomePhoneType)(98=FaxNumberType)(99=PagerNumberType)
(9A=CellularNumberType)(9B=HomeAddress)(9C=HomeAddress2)(9D=HomeCity)
(9E=HomeState)(9F=HomeZipCode)(A0=HomeCountry)(A1=WorkAddress)
(A2=WorkAddress2)(A3=WorkCity)(A4=WorkState)(A5=WorkZipCode)
(A6=WorkCountry)(A7=JobTitle)(A8=Department)(A9=Company)
(AA=_AimScreenName)(AB=AnniversaryYear)(AC=AnniversaryMonth)
(AD=AnniversaryDay)(AE=SpouseName)(AF=FamilyName)(B0=DefaultAddress)
(B1=Category)(B2=WebPage1)(B3=WebPage2)(B4=BirthYear)(B5=BirthMonth)
(B6=BirthDay)(B7=Custom1)>
<(A8=8)(81=firs)(82=lastn)(83=)(84=d)(85=ni)(86=ema@test.invalid)(80=0)
(87=1)(88=first)(89=l)(8A=di)(8B=nic)(8C=emai@test.invalid)(8D=2)(8E=f)
(8F=la)(90=dis)(91=nick)(92=email@test.invalid)(93=3)(94=fi)(95=las)
(96=disp)(97=nickn)(98=e@test.invalid)(99=4)(9A=fir)(9B=last)(9C=displ)
(9D=n)(9E=em@test.invalid)(9F=5)(A0=t)(A1=list)(A2=6)(A3=te)(A4=lis)
(A5=7)(A6=tes)(A7=li)>
{1:^80 {(k^C0:c)(s=9)}
[1:^82(^BF=8)]
[1(^83^81)(^84^82)(^85=)(^86=)(^87=d)(^88=ni)(^89^86)(^8A^86)(^8B=)
(^8C=)(^8D=)(^8E=0)(^8F=0)(^90=0)(^91=)(^92=)(^93=)(^94=)(^95=)
(^96=)(^97=)(^98=)(^99=)(^9A=)(^9B=)(^9C=)(^9D=)(^9E=)(^9F=)(^A0=)
(^A1=)(^A2=)(^A3=)(^A4=)(^A5=)(^A6=)(^A7=)(^A8=)(^A9=)(^AA=)(^AB=)
(^AC=)(^AD=)(^AE=)(^AF=)(^B0=)(^B1=)(^B2=)(^B3=)(^B4=)(^B5=)(^B6=)
(^B7=)(^B8=)(^B9=)(^BA=)(^BB=)(^BC=0)(^BD=1)]
[2(^83^88)(^84=l)(^85=)(^86=)(^87=di)(^88^8B)(^89^8C)(^8A^8C)(^8B=)
(^8C=)(^8D=)(^8E=0)(^8F=0)(^90=0)(^91=)(^92=)(^93=)(^94=)(^95=)
(^96=)(^97=)(^98=)(^99=)(^9A=)(^9B=)(^9C=)(^9D=)(^9E=)(^9F=)(^A0=)
(^A1=)(^A2=)(^A3=)(^A4=)(^A5=)(^A6=)(^A7=)(^A8=)(^A9=)(^AA=)(^AB=)
(^AC=)(^AD=)(^AE=)(^AF=)(^B0=)(^B1=)(^B2=)(^B3=)(^B4=)(^B5=)(^B6=)
(^B7=)(^B8=)(^B9=)(^BA=)(^BB=)(^BC=0)(^BD=2)]
[3(^83=f)(^84=la)(^85=)(^86=)(^87^90)(^88^91)(^89^92)(^8A^92)(^8B=)
(^8C=)(^8D=)(^8E=0)(^8F=0)(^90=0)(^91=)(^92=)(^93=)(^94=)(^95=)
(^96=)(^97=)(^98=)(^99=)(^9A=)(^9B=)(^9C=)(^9D=)(^9E=)(^9F=)(^A0=)
(^A1=)(^A2=)(^A3=)(^A4=)(^A5=)(^A6=)(^A7=)(^A8=)(^A9=)(^AA=)(^AB=)
(^AC=)(^AD=)(^AE=)(^AF=)(^B0=)(^B1=)(^B2=)(^B3=)(^B4=)(^B5=)(^B6=)
(^B7=)(^B8=)(^B9=)(^BA=)(^BB=)(^BC=0)(^BD=3)]
[4(^83=fi)(^84^95)(^85=)(^86=)(^87^96)(^88^97)(^89^98)(^8A^98)(^8B=)
(^8C=)(^8D=)(^8E=0)(^8F=0)(^90=0)(^91=)(^92=)(^93=)(^94=)(^95=)
(^96=)(^97=)(^98=)(^99=)(^9A=)(^9B=)(^9C=)(^9D=)(^9E=)(^9F=)(^A0=)
(^A1=)(^A2=)(^A3=)(^A4=)(^A5=)(^A6=)(^A7=)(^A8=)(^A9=)(^AA=)(^AB=)
(^AC=)(^AD=)(^AE=)(^AF=)(^B0=)(^B1=)(^B2=)(^B3=)(^B4=)(^B5=)(^B6=)
(^B7=)(^B8=)(^B9=)(^BA=)(^BB=)(^BC=0)(^BD=4)]
[5(^83^9A)(^84^9B)(^85=)(^86=)(^87^9C)(^88=n)(^89^9E)(^8A^9E)(^8B=)
(^8C=)(^8D=)(^8E=0)(^8F=1)(^90=0)(^91=)(^92=)(^93=)(^94=)(^95=)
(^96=)(^97=)(^98=)(^99=)(^9A=)(^9B=)(^9C=)(^9D=)(^9E=)(^9F=)(^A0=)
(^A1=)(^A2=)(^A3=)(^A4=)(^A5=)(^A6=)(^A7=)(^A8=)(^A9=)(^AA=)(^AB=)
(^AC=)(^AD=)(^AE=)(^AF=)(^B0=)(^B1=)(^B2=)(^B3=)(^B4=)(^B5=)(^B6=)
(^B7=)(^B8=)(^B9=)(^BA=)(^BB=)(^BC=0)(^BD=5)]
[1:^81(^C1=t)(^C5=t)(^C2=)(^C3^A1)(^C4=0)(^BD=6)]
[2:^81(^C1=te)(^C5=te)(^C2=)(^C3^A4)(^C4=0)(^BD=7)]
[3:^81(^C1^A6)(^C5^A6)(^C2=)(^C3=li)(^C4=0)(^BD=8)]}
@$${7{@
<(AB=9)(A9=test)(AA=abcdef)>
{1:^80 {(k^C0:c)(s=9)}
[-4:^81(^C1^A9)(^C5^A9)(^C2^AA)(^C3=l)(^C4=0)(^BD=9)]}
[1:^82(^BF=9)]
@$$}7}@
@$${9{@
<(AC=4b683ded)>[-5:^80(^83^9A)(^84^9B)(^85=)(^86=)(^87^9C)(^88=n)(^89^9E)
(^8A^9E)(^8B=)(^8C=)(^8D=)(^8E=0)(^8F=9)(^90=0)(^91=)(^92=)(^93=)
(^94=)(^95=)(^96=)(^97=)(^98=)(^99=)(^9A=)(^9B=)(^9C=)(^9D=)(^9E=)
(^9F=)(^A0=)(^A1=)(^A2=)(^A3=)(^A4=)(^A5=)(^A6=)(^A7=)(^A8=)(^A9=)
(^AA=)(^AB=)(^AC=)(^AD=)(^AE=)(^AF=)(^B0=)(^B1=)(^B2=)(^B3=)(^B4=)
(^B5=)(^B6=)(^B7=)(^B8=)(^B9=)(^BA=)(^BB=)(^BC^AC)(^BD=5)]
@$$}9}@
@$${B{@
<(AD=a)(AE=4b6840f5)>[-5:^80(^83^9A)(^84^9B)(^85=)(^86=)(^87^9C)(^88=n)
(^89^9E)(^8A^9E)(^8B=)(^8C=)(^8D=)(^8E=0)(^8F=a)(^90=0)(^91=)(^92=)
(^93=)(^94=)(^95=)(^96=)(^97=)(^98=)(^99=)(^9A=)(^9B=)(^9C=)(^9D=)
(^9E=)(^9F=)(^A0=)(^A1=)(^A2=)(^A3=)(^A4=)(^A5=)(^A6=)(^A7=)(^A8=)
(^A9=)(^AA=)(^AB=)(^AC=)(^AD=)(^AE=)(^AF=)(^B0=)(^B1=)(^B2=)(^B3=)
(^B4=)(^B5=)(^B6=)(^B7=)(^B8=)(^B9=)(^BA=)(^BB=)(^BC^AE)(^BD=5)]
@$$}B}@