diff options
Diffstat (limited to 'mailnews/db/gloda/modules/utils.js')
-rw-r--r-- | mailnews/db/gloda/modules/utils.js | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/mailnews/db/gloda/modules/utils.js b/mailnews/db/gloda/modules/utils.js new file mode 100644 index 000000000..2a095c427 --- /dev/null +++ b/mailnews/db/gloda/modules/utils.js @@ -0,0 +1,155 @@ +/* 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.EXPORTED_SYMBOLS = ['GlodaUtils']; + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cr = Components.results; +var Cu = Components.utils; + +Cu.import("resource:///modules/mailServices.js"); + +/** + * @namespace A holding place for logic that is not gloda-specific and should + * reside elsewhere. + */ +var GlodaUtils = { + + /** + * This Regexp is super-complicated and used at least in two different parts of + * the code, so let's expose it from one single location. + */ + PART_RE: new RegExp("^[^?]+\\?(?:/;section=\\d+\\?)?(?:[^&]+&)*part=([^&]+)(?:&[^&]+)*$"), + + deMime: function gloda_utils_deMime(aString) { + return MailServices.mimeConverter.decodeMimeHeader(aString, null, false, true); + }, + + _headerParser: MailServices.headerParser, + + /** + * Parses an RFC 2822 list of e-mail addresses and returns an object with + * 4 attributes, as described below. We will use the example of the user + * passing an argument of '"Bob Smith" <bob@example.com>'. + * + * This method (by way of nsIMsgHeaderParser) takes care of decoding mime + * headers, but is not aware of folder-level character set overrides. + * + * count: the number of addresses parsed. (ex: 1) + * addresses: a list of e-mail addresses (ex: ["bob@example.com"]) + * names: a list of names (ex: ["Bob Smith"]) + * fullAddresses: aka the list of name and e-mail together (ex: ['"Bob Smith" + * <bob@example.com>']). + * + * This method is a convenience wrapper around nsIMsgHeaderParser. + */ + parseMailAddresses: function gloda_utils_parseMailAddresses(aMailAddresses) { + let addresses = {}, names = {}, fullAddresses = {}; + this._headerParser.parseHeadersWithArray(aMailAddresses, addresses, + names, fullAddresses); + return {names: names.value, addresses: addresses.value, + fullAddresses: fullAddresses.value, + count: names.value.length}; + }, + + /** + * MD5 hash a string and return the hex-string result. Impl from nsICryptoHash + * docs. + */ + md5HashString: function gloda_utils_md5hash(aString) { + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. + createInstance(Ci.nsIScriptableUnicodeConverter); + let trash = {}; + converter.charset = "UTF-8"; + let data = converter.convertToByteArray(aString, trash); + + let hasher = Cc['@mozilla.org/security/hash;1']. + createInstance(Ci.nsICryptoHash); + hasher.init(Ci.nsICryptoHash.MD5); + hasher.update(data, data.length); + let hash = hasher.finish(false); + + // return the two-digit hexadecimal code for a byte + function toHexString(charCode) { + return ("0" + charCode.toString(16)).slice(-2); + } + + // convert the binary hash data to a hex string. + let hex = Object.keys(hash).map(i => toHexString(hash.charCodeAt(i))); + return hex.join(""); + }, + + getCardForEmail: function gloda_utils_getCardForEmail(aAddress) { + // search through all of our local address books looking for a match. + let enumerator = MailServices.ab.directories; + let cardForEmailAddress; + let addrbook; + while (!cardForEmailAddress && enumerator.hasMoreElements()) + { + addrbook = enumerator.getNext().QueryInterface(Ci.nsIAbDirectory); + try + { + cardForEmailAddress = addrbook.cardForEmailAddress(aAddress); + if (cardForEmailAddress) + return cardForEmailAddress; + } catch (ex) {} + } + + return null; + }, + + _FORCE_GC_AFTER_NUM_HEADERS: 4096, + _headersSeen: 0, + /** + * As |forceGarbageCollection| says, once XPConnect sees a header, it likes + * to hold onto that reference. This method is used to track the number of + * headers we have seen and force a GC when we have to. + * + * Ideally the indexer's idle-biased GC mechanism would take care of all the + * GC; we are just a failsafe to make sure that our memory usage is bounded + * based on the number of headers we have seen rather than just time. + * Since holding onto headers can keep databases around too, this also + * helps avoid keeping file handles open, etc. + * + * |forceGarbageCollection| will zero our tracking variable when a GC happens + * so we are informed by the indexer's GC triggering. + * + * And of course, we don't want to trigger collections willy nilly because + * they have a cost even if there is no garbage. + * + * @param aNumHeadersSeen The number of headers code has seen. A granularity + * of hundreds of messages should be fine. + */ + considerHeaderBasedGC: function(aNumHeadersSeen) { + this._headersSeen += aNumHeadersSeen; + if (this._headersSeen >= this._FORCE_GC_AFTER_NUM_HEADERS) + this.forceGarbageCollection(); + }, + + /** + * Force a garbage-collection sweep. Gloda has to force garbage collection + * periodically because XPConnect's XPCJSRuntime::DeferredRelease mechanism + * can end up holding onto a ridiculously high number of XPConnect objects in + * between normal garbage collections. This has mainly posed a problem + * because nsAutolock is a jerk in DEBUG builds in 1.9.1, but in theory this + * also helps us even out our memory usage. + * We also are starting to do this more to try and keep the garbage collection + * durations acceptable. We intentionally avoid triggering the cycle + * collector in those cases, as we do presume a non-trivial fixed cost for + * cycle collection. (And really all we want is XPConnect to not be a jerk.) + * This method exists mainly to centralize our GC activities and because if + * we do start involving the cycle collector, that is a non-trivial block of + * code to copy-and-paste all over the place (at least in a module). + * + * @param aCycleCollecting Do we need the cycle collector to run? Currently + * unused / unimplemented, but we would use + * nsIDOMWindowUtils.garbageCollect() to do so. + */ + forceGarbageCollection: + function gloda_utils_garbageCollection(aCycleCollecting) { + Cu.forceGC(); + this._headersSeen = 0; + } +}; |