Hi friends of Thunderbird and connoisseurs of "URLs for everything" ;-)
URLs in Thunderbird have had a very tough time recently, since M-C made
the underlying class nsIURI immutable, that means all setter methods
were withdrawn. If you want to get a URL with a different attribute, you
need to make a clone and in the process of cloning change the attribute.
This is now done via so-called mutators and looks like this in C++
NS_MutateURI(url1).SetSpec(aSpec).Finalize(url2);
or like this in JS:
url2 = url1.mutate().setSpec(aSpec).finalize();
where url1 and url2 can be the same variable. There is a lot of fine
print, be let's ignore that here.
TB has four URL classes, one of which has many derived classes. The
three simple URL classes are nsMailtoUrl, nsLDAPURL and nsAddbookUrl
which extend nsIURI and have no derived classes, and nsMsgMailNewsUrl
from which we derive SMTP, IMAP, JSAccount, Mailbox, POP and NNTP URLs.
nsMsgMailNewsUrl extends nsIURL (not nsIURI).
In Thunderbird, "extension" is done via composition[1], that means, the
classes carry a member variable, typically called 'm_baseURL' to
reference an instance of the class being extended, to with many methods
are forwarded, like for example:
NS_IMETHODIMP nsAddbookUrl::GetPort(int32_t *aPort) {
return m_baseURL->GetPort(aPort);
}
Now that the extended classes are immutable, that means that the setter
methods look like this:
nsresult nsAddbookUrl::SetPort(int32_t aPort) {
return NS_MutateURI(m_baseURL).SetPort(aPort).Finalize(m_baseURL);
}
So far so good. "But hold on", the astute reader will say, "won't
cloning make existing references stale?". The answer is "yes". Take for
example:
nsCOMPtr<nsIURI> currentURL = url; // Keep a pointer to the URL we're
currently processing.
NS_MutateURI(url).SetQuery("number=1").Finalize(url);
Oops, currentURL is now stale pointing the previous URL object before
the clone. To add insult to injury, Thunderbird's URLs also hold a lot
of state information in additional member variables of the object, so if
'url' in the example were to be processed further to adding some
listeners, the 'currentURL' wouldn't hear about those changes.
To work around the problem, a few "internal" methods were introduced on
nsIMsgMailNewsUrl: SetSpecInternal, SetPortInternal and
SetQueryInternal. These methods are available to C++ code and allow
setting attributes of the underlying nsIURL object by cloning it (see
"composition" above), without cloning the nsMsgMailNewsUrl object.
Note we easily confuse interfaces (nsIMsgMailNewsUrl) and classes
(nsMsgMailNewsUrl) implementing them, and there is of course no "nsIURL
object", the class that implements nsIURL in Mozilla is class
nsStandardURL, which in the new scheme is created like this:
nsCOMPtr<nsIURL> url;
NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID).SetSpec(urlstr).Finalize(url);
But that is material for another post.
The URL work is now complete and Thunderbird still works, as you can see
here and by looking at a mostly green tree :-)
Jörg.
P.S.: The horrible details can be found in these two bugs:
https://bugzilla.mozilla.org/show_bug.cgi?id=1431913
https://bugzilla.mozilla.org/show_bug.cgi?id=1440693
P.S.2: Thanks to Kent for interrupting his retirement to take care of
JSAccount URLs. Kent, please try ExQuilla now!
[1] https://en.wikipedia.org/wiki/Composition_over_inheritance