in mail/components/extensions/parent/ext-folders.js [624:998]
async query(queryInfo) {
// Generator function to flatten the folder structure.
function* getFlatFolderStructure(folder) {
yield folder;
if (folder.hasSubFolders) {
for (const subFolder of folder.subFolders) {
yield* getFlatFolderStructure(subFolder);
}
}
}
// Evaluate query properties which can be specified as boolean
// (none/some) or integer (min/max).
function matchBooleanOrQueryRange(query, valueCallback) {
if (query == null) {
return true;
}
const value = valueCallback();
if (typeof query == "boolean") {
return query == (value != 0);
}
// If not a boolean, it is an object with min and max members.
if (query.min != null && value < query.min) {
return false;
}
if (query.max != null && value > query.max) {
return false;
}
return true;
}
// Prepare query dates.
const queryDates = {};
const recentTime = Date.now() - FolderUtils.ONE_MONTH_IN_MILLISECONDS;
if (
queryInfo.recent === true ||
queryInfo.lastUsed?.recent === true
) {
queryDates.lastUsedAfter = recentTime;
} else if (
queryInfo.recent === false ||
queryInfo.lastUsed?.recent === false
) {
queryDates.lastUsedBefore = recentTime;
} else {
if (queryInfo.lastUsed?.after) {
queryDates.lastUsedAfter = queryInfo.lastUsed.after.getTime();
}
if (queryInfo.lastUsed?.before) {
queryDates.lastUsedBefore = queryInfo.lastUsed.before.getTime();
}
}
if (queryInfo.lastUsedAsDestination?.recent === true) {
queryDates.lastUsedAsDestinationAfter = recentTime;
} else if (queryInfo.lastUsedAsDestination?.recent === false) {
queryDates.lastUsedAsDestinationBefore = recentTime;
} else {
if (queryInfo.lastUsedAsDestination?.after) {
queryDates.lastUsedAsDestinationAfter =
queryInfo.lastUsedAsDestination.after.getTime();
}
if (queryInfo.lastUsedAsDestination?.before) {
queryDates.lastUsedAsDestinationBefore =
queryInfo.lastUsedAsDestination.before.getTime();
}
}
// Prepare folders, which are to be searched.
const parentFolders = [];
if (queryInfo.folderId) {
const { folder, accountKey } = getFolder(queryInfo.folderId);
if (!queryInfo.accountId || queryInfo.accountId == accountKey) {
parentFolders.push({
rootFolder: folder,
accountId: accountKey,
});
}
} else if (queryInfo.isUnified || queryInfo.isTag) {
const smartMailbox = SmartMailboxUtils.getSmartMailbox();
// Unified mailbox folders are disjunct to virtual tag folders.
// Manually suppress the case both are specified.
if (
smartMailbox.account &&
queryInfo.isUnified &&
!queryInfo.isTag
) {
const allowedFolderFlags =
SmartMailboxUtils.getFolderTypes().reduce(
(acc, { flag }) => acc | flag,
0
);
for (const folder of smartMailbox.rootFolder.subFolders) {
// Require unified folders to have an allowed folder type.
if (allowedFolderFlags & folder.flags) {
parentFolders.push({
rootFolder: folder,
accountId: smartMailbox.account.key,
});
}
}
}
if (
smartMailbox.account &&
!queryInfo.isUnified &&
queryInfo.isTag
) {
const tags = MailServices.tags.getAllTags();
for (const tag of tags) {
const folder = smartMailbox.getTagFolder(tag);
if (!folder) {
continue;
}
parentFolders.push({
rootFolder: folder,
accountId: smartMailbox.account.key,
});
}
}
} else {
for (const account of getMailAccounts()) {
const accountId = account.key;
if (!queryInfo.accountId || queryInfo.accountId == accountId) {
parentFolders.push({
rootFolder: account.incomingServer.rootFolder,
accountId,
});
}
}
}
// Prepare usage flags.
const specialUse =
!queryInfo.specialUse && queryInfo.type && manifestVersion < 3
? [queryInfo.type]
: queryInfo.specialUse;
const specialUseFlags =
specialUse && Array.isArray(specialUse) && specialUse.length > 0
? [...specialUseMap.entries()]
.filter(([, specialUseName]) =>
specialUse.includes(specialUseName)
)
.map(([flag]) => flag)
.reduce((rv, f) => rv | f)
: null;
// Prepare regular expression for the name.
let nameRegExp;
if (queryInfo.name != null && queryInfo.name.regexp) {
try {
nameRegExp = new RegExp(
queryInfo.name.regexp,
queryInfo.name.flags || undefined
);
} catch (ex) {
throw new ExtensionError(
`Invalid Regular Expression: ${JSON.stringify(queryInfo.name)}`
);
}
}
// Prepare regular expression for the path.
let pathRegExp;
if (queryInfo.path != null && queryInfo.path.regexp) {
try {
pathRegExp = new RegExp(
queryInfo.path.regexp,
queryInfo.path.flags || undefined
);
} catch (ex) {
throw new ExtensionError(
`Invalid Regular Expression: ${JSON.stringify(queryInfo.path)}`
);
}
}
let foundFolders = [];
for (const parentFolder of parentFolders) {
const { accountId, rootFolder } = parentFolder;
for (const folder of getFlatFolderStructure(rootFolder)) {
// Apply lastUsed search criteria.
if (
queryDates.lastUsedAfter &&
!(getFolderTime(folder, "MRUTime") > queryDates.lastUsedAfter)
) {
continue;
}
if (
queryDates.lastUsedBefore &&
!(getFolderTime(folder, "MRUTime") < queryDates.lastUsedBefore)
) {
continue;
}
// Apply lastUsedAsDestination search criteria.
if (
queryDates.lastUsedAsDestinationAfter &&
!(
getFolderTime(folder, "MRMTime") >
queryDates.lastUsedAsDestinationAfter
)
) {
continue;
}
if (
queryDates.lastUsedAsDestinationBefore &&
!(
getFolderTime(folder, "MRMTime") <
queryDates.lastUsedAsDestinationBefore
)
) {
continue;
}
if (
queryInfo.isFavorite != null &&
queryInfo.isFavorite !=
!!folder.getFlag(Ci.nsMsgFolderFlags.Favorite)
) {
continue;
}
const isServer = folder.isServer;
if (queryInfo.isRoot != null && queryInfo.isRoot != isServer) {
continue;
}
if (
queryInfo.isUnified != null &&
queryInfo.isUnified !=
(folder.server.hostName == "smart mailboxes")
) {
continue;
}
if (
queryInfo.isVirtual != null &&
queryInfo.isVirtual !=
folder.getFlag(Ci.nsMsgFolderFlags.Virtual)
) {
continue;
}
if (specialUseFlags && ~folder.flags & specialUseFlags) {
continue;
}
if (
!matchBooleanOrQueryRange(queryInfo.hasMessages, () =>
isServer ? 0 : folder.getTotalMessages(false)
)
) {
continue;
}
if (
!matchBooleanOrQueryRange(queryInfo.hasNewMessages, () =>
isServer ? 0 : folder.msgDatabase.getNewList().length
)
) {
continue;
}
if (
!matchBooleanOrQueryRange(queryInfo.hasUnreadMessages, () =>
isServer ? 0 : folder.getNumUnread(false)
)
) {
continue;
}
if (
!matchBooleanOrQueryRange(
queryInfo.hasSubFolders,
() => folder.subFolders?.length || 0
)
) {
continue;
}
if (
queryInfo.canAddMessages != null &&
queryInfo.canAddMessages != folder.canFileMessages
) {
continue;
}
if (
queryInfo.canAddSubfolders != null &&
queryInfo.canAddSubfolders != folder.canCreateSubfolders
) {
continue;
}
if (
queryInfo.canBeDeleted != null &&
queryInfo.canBeDeleted != FolderManager.canBeDeleted(folder)
) {
continue;
}
if (
queryInfo.canBeRenamed != null &&
queryInfo.canBeRenamed != FolderManager.canBeRenamed(folder)
) {
continue;
}
if (
queryInfo.canDeleteMessages != null &&
queryInfo.canDeleteMessages != folder.canDeleteMessages
) {
continue;
}
if (queryInfo.name) {
const name = isServer ? "Root" : folder.prettyName;
if (nameRegExp) {
if (!nameRegExp.test(name)) {
continue;
}
} else if (queryInfo.name != name) {
continue;
}
}
if (queryInfo.path) {
const folderPath = folderURIToPath(accountId, folder.URI);
if (pathRegExp) {
if (!pathRegExp.test(folderPath)) {
continue;
}
} else if (queryInfo.path != folderPath) {
continue;
}
}
foundFolders.push(folder);
}
}
// Apply sort.
if (queryInfo.recent || queryInfo.sort == "lastUsed") {
foundFolders = sortFoldersByTime(foundFolders, "MRUTime");
} else if (queryInfo.sort == "lastUsedAsDestination") {
foundFolders = sortFoldersByTime(foundFolders, "MRMTime");
} else if (queryInfo.sort == "name") {
foundFolders.sort((a, b) =>
a.prettyName.localeCompare(b.prettyName, undefined, {
sensitivity: "base",
})
);
}
// Apply limit. It can be set to -1 = mail.folder_widget.max_recent for
// recent queries.
if (queryInfo.limit) {
const recentQuery =
queryInfo.recent ||
queryInfo.lastUsed?.recent ||
queryInfo.lastUsedAsDestination?.recent;
const limit =
queryInfo.limit == -1 && recentQuery
? Services.prefs.getIntPref("mail.folder_widget.max_recent")
: queryInfo.limit;
if (limit > 0) {
foundFolders.splice(limit);
}
}
return foundFolders.map(folder =>
context.extension.folderManager.convert(folder)
);
},