Migrating from Internal Linkage to Frozen Linkage

Extension code using internal linkage will need to migrate to use frozen linkage because internal linkage will not be available in Firefox 3. This document is a guide to common code patterns that may need to change to work with frozen linkage.

Strings

The most obvious change required by frozen linkage is using the frozen string API.

- #include "nsAString.h"
- #include "nsString.h"
- #include "nsReadableUtils.h"
- #include "nsEscape.h"
+ #include "nsStringAPI.h"

On Windows, if you see the following error, you are including a header you shouldn't be:

nsStringFwd.h(60) : fatal error C1001: INTERNAL COMPILER ERROR

To debug this error, make in the failing directory, adding the /showIncludes directive to figure out what is being included incorrectly:

make -C directory/that/failed OS_CPPFLAGS=-showIncludes

The frozen string API is similar but not identical to the nonfrozen string API. The frozen string API does not have (or need) nsXPIDLString:

- nsXPIDLString value;
+ nsString value;
  ptr->GetterMethod(getter_Copies(value));
- const PRUnichar *strvalue = value;
+ // nsString doesn't cast directly to PRUnichar*, use .get()
+ const PRUnichar *strvalue = value.get();

The frozen string API doesn't accept a length for .Truncate(). Use .SetLength() instead:

  nsString myString = someString;
- myString.Truncate(4);
+ myString.SetLength(4);

The frozen string API doesn't support the iterator or const_iterator classes, but you can use pointers the same way:

  nsString myString = someString;
- nsString::const_iterator begin, end;
- myString.BeginReading(begin); myString.EndReading(end);
+ const PRUnichar *begin, *end;
+ myString.BeginReading(&begin, &end);

The frozen string API uses comparator functions instead of a virtual comparator class. unicharutils has been modified to provide a frozen-linkage comparator:

  nsString myString = someString;
- if (myString.Equals(otherString, nsCaseInsensitiveStringComparator()))
+ if (myString.Equals(otherString, CaseInsensitiveCompare))
    return NS_OK; // woot, we're equal in all things but case
When using unicharutils from frozen-linkage code, link against the unicharutil_external_s static library.

The frozen string API doesn't implement the Left(), Mid(), or Right() signatures. Use StringHead(), Substring(), and StringTail() instead:

  nsString buffer = someString;
- nsCAutoString prefix;
- buffer.Left(prefix, 4);
+ const nsDependentSubstring prefix = StringHead(buffer, 4);

The frozen string API doesn't use nsSubstringTuple objects and maintains all strings in a contiguous buffer. This means that the + operator is not implemented, nor are the PromiseFlatString functions or classes around any more. Use += or Append() instead of using +, and use nsString or nsCString instead of PromiseFlatString:

  nsString firstString = someString;
  nsString secondString = someOtherString;
- nsString comboString = firstString + secondString;
+ nsString comboString = firstString;
+ comboString += secondString; // or: comboString.Append(secondString);
- nsresult rv = SomeFunc(PromiseFlatString(comboString)); + nsresult rv = SomeFunc(comboString);

Removing the nsReadableUtils.h from the headers list also means that we would not have access to AppendUTF16toUTF8 kind of functions. All these functions are now available via the new String API

 - AppendUTF16toUTF8(srcString, destString);
 + destString.Append(NS_ConvertUTF16toUTF8(srcString));

The signatures of the Find methods differ between the two APIs. If you are only finding a single character, prefer the FindChar method.

The (Lossy)Copy(ASCII|UTF8|16)to(ASCII|UTF8|16) do not accept character pointer parameters. You must explicitly wrap them in an appropriate dependent string.

You cannot make a dependent string from an abstract string. Nor can you use nsPromiseFlat(C)String. You probably meant to use PromiseFlat(C)String instead. Note that you don't need to promise a flat string if you already have an ns(C)(Auto)String type, since they are already flat.

You cannot mix string variables and literals in comparison operators. In particular you may need to use NS_LITERAL_(C)STRING("") instead of Empty(C)String.

The EqualsIgnoreCase method does not take a length parameters. You may be able to use StringBeginsWith instead.

The FindInReadable methods do not exist in the external API. You may be able to use the Find methods instead.

The ToNewCString, ToNewUnicode methods only work on the appropriate string class. You need to supply the appropriate conversion yourself.

The Adopt and getter_Copies methods do not work on abstract strings. Instead of passing getter_Copies(aString) to a method expecting a character pointer out parameter, you will need to use a temporary variable and copy the result.

Missing Headers

Some headers are included from IDL files only when MOZILLA_INTERNAL_API is defined (actually, they shouldn't be there at all).

For errors about undeclared do_CreateInstance:

#include "nsComponentManagerUtils.h"

For errors about undeclared do_GetService:

#include "nsServiceManagerUtils.h"

For errors about undeclared NS_GetSpecialDirectory:

#include "nsDirectoryServiceUtils.h"

Utility Classes

Some utility classes could previously be created with NS_New* utility functions. These classes must now be created using XPCOM:

  // nsISupportsArray is bad! Don't use it in new code. Use nsIArray instead. (See XPCOM:Arrays.)
  nsCOMPtr<nsISupportsArray> array;
- rv = NS_NewISupportsArray(getter_AddRefs(array));
+ array = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
- nsCOMPtr<nsIInputStream> rawStream;
- rv = NS_NewByteInputStream(getter_AddRefs(rawStream),
- (const char*)data, length);
+ nsCOMPtr<nsIStringInputStream> rawStream =
+ do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = rawStream->SetData((const char*)data, length);
NS_ENSURE_SUCCESS(rv, rv);

nsIStringInputStream is not frozen (and thus, not available in the Gecko SDK as currently published). In order to use this interface in your code, you will need to get the IDL files from the source (via CVS, from a tarball, or using LXR).

The following functions, however, are implemented in the glue library and can be used from frozen-linkage code:

  • NS_NewArrayEnumerator (excluding deprecated nsISupportsArray version)
  • NS_NewEmptyEnumerator
  • NS_NewUnionEnumerator

nsCRT

Functions in nsCRT.h are not available to frozen-linkage code. Some of the constants are still useful though.

- #include "nsCRT.h"
+ #include <string.h>

  const char *str = "Foo";
- PRUint32 len = nsCRT::strlen(str);
+ PRUint32 len = strlen(str);
- #include "nsCRT.h"
+ #include "nsCRTGlue.h"

  const PRUnichar str[] = {'F','o','o','\0'};
- PRUint32 len = nsCRT::strlen(str);
+ PRUint32 len = NS_strlen(str);
- #include "nsCRT.h"
+ #include "nsMemory.h"
+ #include "nsCRTGlue.h"

  PRUnichar* anotherstr = (PRUnichar*) NS_Alloc(100 * sizeof(PRUnichar));
- PRUnichar *str = nsCRT::strdup(anotherstr);
- nsCRT::free(str);
+ PRUnichar *str = NS_strdup(anotherstr);
+ NS_Free(str);

Linking

For information about the correct libraries to link to when using frozen linkage, see XPCOM Glue.

The nsMsgUtils.h header

For developing core Mail/News code, the nsMsgUtils.h header is being enhanced to fill in some of the gaps caused by the differences between the internal and external APIs. These enhancements make it easier to write code that will transparently compile using either API. The list of enhancements fall in to three categories.

Changes to internal APIs to make them compatible with the external API.

  • #define CaseInsensitiveCompare PR_TRUE
    This allows you to use CaseInsensitiveCompare as the parameter to various string methods that normally accept PR_TRUE under the internal API.
  • #define ToInteger(prv, radix) ToInteger(reinterpret_cast<PRInt32*>(prv), radix)
    This allows you to pass a pointer to an nsresult to the ToInteger method, when it normally accepts a PRInt32. Note that you must pass the radix (normally 10).

Changes to external APIs to make them compatible with the internal API.

  • #define nsCaseInsensitiveCStringComparator() CaseInsensitiveCompare
  • #define nsCaseInsensitiveStringComparator() CaseInsensitiveCompare
    These allow you to use the comparators to those APIs that normally accept CaseInsensitiveCompare under the external API.
  • #define kNotFound -1
    This convenience constant is not defined under the external API.
  • #define AppendASCII(str) AppendLiteral(str)
    The internal API distinguishes between literals (which must be character constants) and ASCII strings. The external API confusingly provides an AppendLiteral method that does not require a character constant.
  • #define AppendUTF16toUTF8(a16, a8) (a8).Append(NS_ConvertUTF16toUTF8(a16))
    And similarly for the other conversions.
  • #define Compare(str1, str2, comp) (str1).Compare(str2, comp)
  • #define Last() EndReading()[-1]
  • #define SetCharAt(c, i) Replace(i, 1, ch)
  • #define NS_NewISupportsArray(result) CallCreateInstance(NS_SUPPORTSARRAY_CONTRACTID, static_cast<nsISupportsArray**>(result));
    Ideally we would switch to nsIMutableArray, but in the mean time there's no point changing the same code twice.
    Note: #include nsISupportsArray.h before nsMsgUtils.h

Convenience methods that work using either API.

  • MsgLowerCaseEqualsLiteral(a8, literal)
    Replaces (a8).LowerCaseEqualsLiteral(literal). Note that the external API provides LowerCaseEqualsLiteral for nsAString, but not nsACString.
  • MsgRFindChar(str, ch, len)
    Replaces (str).RFindChar(ch, len)
  • MsgCompressWhitespace(str)
    Replaces (str).CompressWhitespace(). Support for the optional parameters is not provided.
  • MsgEscapeHTML(str)
    Replaces nsEscapeHTML(str)
  • MsgEscapeHTML2(buffer, len)
    Replaces nsEscapeHTML2(buffer, len). Support for the other forms of nsEscapeHTML is not provided.