diff options
Diffstat (limited to 'comm/mailnews/base/src/FolderLookupService.jsm')
-rw-r--r-- | comm/mailnews/base/src/FolderLookupService.jsm | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/comm/mailnews/base/src/FolderLookupService.jsm b/comm/mailnews/base/src/FolderLookupService.jsm new file mode 100644 index 0000000000..9530dc6e6e --- /dev/null +++ b/comm/mailnews/base/src/FolderLookupService.jsm @@ -0,0 +1,163 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This module implements the folder lookup service (nsIFolderLookupService). + */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["FolderLookupService"]; + +// This ensures that the service is only created once. +var gCreated = false; + +/** + * FolderLookupService maintains an index of folders and provides + * lookup by folder URI. + * + * @class + */ +function FolderLookupService() { + if (gCreated) { + throw Components.Exception("", Cr.NS_ERROR_ALREADY_INITIALIZED); + } + this._map = new Map(); + gCreated = true; +} + +FolderLookupService.prototype = { + QueryInterface: ChromeUtils.generateQI(["nsIFolderLookupService"]), + + /** + * Fetch the folder corresponding to the given URI. + * Will only return folders which already exist and have a parent. If this + * not the case then null is returned. + * + * @param {string} uri - URI of folder to get. + * @returns {nsIMsgFolder|null} + */ + getFolderForURL(uri) { + let folder = this._getExisting(uri); + + if (folder && !this._isValidFolder(folder)) { + folder = null; // no dangling folders! + } + return folder; + }, + + /** + * Fetch the folder corresponding to the given URI, creating it if it does + * not exist. If the folder is created, it will be a "dangling" folder, + * without a parent and not part of a normal folder hierarchy. + * A lot of code relies on this behaviour, but for new code this + * call should probably be avoided. + * + * @param {string} uri - URI of folder to get. + * @returns {nsIMsgFolder} + */ + getOrCreateFolderForURL(uri) { + let folder = this._getExisting(uri); + if (folder) { + return folder; + } + + // Create new folder. + + // Check that uri has an active scheme, in case this folder is from + // an extension that is currently disabled or hasn't started up yet. + let schemeMatch = uri.match(/^([-+.\w]+):/); + if (!schemeMatch) { + return null; + } + let scheme = schemeMatch[1]; + let contractID = "@mozilla.org/mail/folder-factory;1?name=" + scheme; + if (!(contractID in Cc)) { + console.error( + "getOrCreateFolderForURL: factory not registered for " + uri + ); + return null; + } + + let factory = Components.manager.getClassObject( + Cc[contractID], + Ci.nsIFactory + ); + if (!factory) { + console.error( + "getOrCreateFolderForURL: failed to get factory for " + uri + ); + return null; + } + + folder = factory.createInstance(Ci.nsIMsgFolder); + if (folder) { + folder.Init(uri); + // Add the new folder to our map. Store a weak reference instead, so that + // the folder can be closed when necessary. + let weakRef = folder + .QueryInterface(Ci.nsISupportsWeakReference) + .GetWeakReference(); + this._map.set(uri, weakRef); + } + + return folder; + }, + + /** + * Set pretty name again from original name on all folders, + * typically used when locale changes. + */ + setPrettyNameFromOriginalAllFolders() { + for (const val of this._map.values()) { + try { + let folder = val.QueryReferent(Ci.nsIMsgFolder); + folder.setPrettyNameFromOriginal(); + } catch (e) {} + } + }, + + // "private" stuff starts here. + + /** + * Internal helper to find a folder (which may or may not be dangling). + * + * @param {string} uri - URI of folder to look up. + * + * @returns {nsIMsgFolder|null} - The folder, if in the index, else null. + */ + _getExisting(uri) { + let folder = null; + // already created? + if (this._map.has(uri)) { + try { + folder = this._map.get(uri).QueryReferent(Ci.nsIMsgFolder); + } catch (e) { + // The object was deleted, so we can drop it. + this._map.delete(uri); + } + } + return folder; + }, + + /** + * Internal helper function to test if a folder is dangling or parented. + * Because we can return folders that don't exist, and we may be working + * with a deleted folder but we're still holding on to the reference. For + * valid folders, one of two scenarios is true: either the folder has a parent + * (the deletion code clears the parent to indicate its nonvalidity), or the + * folder is a root folder of some server. Getting the root folder may throw + * an exception if we attempted to create a server that doesn't exist, so we + * need to guard for that error. + * + * @returns {boolean} - true if folder valid (and parented). + */ + _isValidFolder(folder) { + try { + return folder.parent != null || folder.rootFolder == folder; + } catch (e) { + return false; + } + }, +}; |