Back to index

lightning-sunbird  0.9+nobinonly
nsPrimitiveHelpers.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corp.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Mike Pinkerton
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 
00041 //
00042 // Part of the reason these routines are all in once place is so that as new
00043 // data flavors are added that are known to be one-byte or two-byte strings, or even
00044 // raw binary data, then we just have to go to one place to change how the data
00045 // moves into/out of the primitives and native line endings.
00046 //
00047 // If you add new flavors that have special consideration (binary data or one-byte
00048 // char* strings), please update all the helper classes in this file.
00049 //
00050 // For now, this is the assumption that we are making:
00051 //  - text/plain is always a char*
00052 //  - anything else is a PRUnichar*
00053 //
00054 
00055 
00056 #include "nsPrimitiveHelpers.h"
00057 #include "nsCOMPtr.h"
00058 #include "nsXPCOM.h"
00059 #include "nsISupportsPrimitives.h"
00060 #include "nsITransferable.h"
00061 #include "nsIComponentManager.h"
00062 #include "nsLinebreakConverter.h"
00063 #include "nsReadableUtils.h"
00064 
00065 #include "nsIServiceManager.h"
00066 #include "nsICharsetConverterManager.h"
00067 // unicode conversion
00068 #  include "nsIPlatformCharset.h"
00069 #include "nsISaveAsCharset.h"
00070 
00071 
00072 //
00073 // CreatePrimitiveForData
00074 //
00075 // Given some data and the flavor it corresponds to, creates the appropriate
00076 // nsISupports* wrapper for passing across IDL boundaries. Right now, everything
00077 // creates a two-byte |nsISupportsString|, except for "text/plain" and native
00078 // platform HTML (CF_HTML on win32)
00079 //
00080 void
00081 nsPrimitiveHelpers :: CreatePrimitiveForData ( const char* aFlavor, void* aDataBuff, 
00082                                                  PRUint32 aDataLen, nsISupports** aPrimitive )
00083 {
00084   if ( !aPrimitive )
00085     return;
00086 
00087   if ( strcmp(aFlavor,kTextMime) == 0 || strcmp(aFlavor,kNativeHTMLMime) == 0 ) {
00088     nsCOMPtr<nsISupportsCString> primitive =
00089         do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
00090     if ( primitive ) {
00091       const char * start = (const char*)aDataBuff;
00092       primitive->SetData(Substring(start, start + aDataLen));
00093       NS_ADDREF(*aPrimitive = primitive);
00094     }
00095   }
00096   else {
00097     nsCOMPtr<nsISupportsString> primitive =
00098         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
00099     if (primitive ) {
00100       // recall that length takes length as characters, not bytes
00101       const PRUnichar* start = (const PRUnichar*)aDataBuff;
00102       primitive->SetData(Substring(start, start + (aDataLen / 2)));
00103       NS_ADDREF(*aPrimitive = primitive);
00104     }  
00105   }
00106 
00107 } // CreatePrimitiveForData
00108 
00109 
00110 //
00111 // CreateDataFromPrimitive
00112 //
00113 // Given a nsISupports* primitive and the flavor it represents, creates a new data
00114 // buffer with the data in it. This data will be null terminated, but the length
00115 // parameter does not reflect that.
00116 //
00117 void
00118 nsPrimitiveHelpers :: CreateDataFromPrimitive ( const char* aFlavor, nsISupports* aPrimitive, 
00119                                                    void** aDataBuff, PRUint32 aDataLen )
00120 {
00121   if ( !aDataBuff )
00122     return;
00123 
00124   if ( strcmp(aFlavor,kTextMime) == 0 ) {
00125     nsCOMPtr<nsISupportsCString> plainText ( do_QueryInterface(aPrimitive) );
00126     if ( plainText ) {
00127       nsCAutoString data;
00128       plainText->GetData ( data );
00129       *aDataBuff = ToNewCString(data);
00130     }
00131   }
00132   else {
00133     nsCOMPtr<nsISupportsString> doubleByteText ( do_QueryInterface(aPrimitive) );
00134     if ( doubleByteText ) {
00135       nsAutoString data;
00136       doubleByteText->GetData ( data );
00137       *aDataBuff = ToNewUnicode(data);
00138     }
00139   }
00140 
00141 }
00142 
00143 
00144 //
00145 // ConvertUnicodeToPlatformPlainText
00146 //
00147 // Given a unicode buffer (flavor text/unicode), this converts it to plain text using
00148 // the appropriate platform charset encoding. |inUnicodeLen| is the length of the input
00149 // string, not the # of bytes in the buffer. The |outPlainTextData| is null terminated, 
00150 // but its length parameter, |outPlainTextLen|, does not reflect that.
00151 //
00152 nsresult
00153 nsPrimitiveHelpers :: ConvertUnicodeToPlatformPlainText ( PRUnichar* inUnicode, PRInt32 inUnicodeLen, 
00154                                                             char** outPlainTextData, PRInt32* outPlainTextLen )
00155 {
00156   if ( !outPlainTextData || !outPlainTextLen )
00157     return NS_ERROR_INVALID_ARG;
00158 
00159   // Get the appropriate unicode encoder. We're guaranteed that this won't change
00160   // through the life of the app so we can cache it.
00161   nsresult rv;
00162   nsCOMPtr<nsIUnicodeEncoder> encoder;
00163 
00164   // get the charset
00165   nsCAutoString platformCharset;
00166   nsCOMPtr <nsIPlatformCharset> platformCharsetService = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
00167   if (NS_SUCCEEDED(rv))
00168     rv = platformCharsetService->GetCharset(kPlatformCharsetSel_PlainTextInClipboard, platformCharset);
00169   if (NS_FAILED(rv))
00170     platformCharset.AssignLiteral("ISO-8859-1");
00171   
00172 
00173   // use transliterate to convert things like smart quotes to normal quotes for plain text
00174 
00175   nsCOMPtr<nsISaveAsCharset> converter = do_CreateInstance("@mozilla.org/intl/saveascharset;1", &rv);
00176   NS_ENSURE_SUCCESS(rv, rv);
00177 
00178   rv = converter->Init(platformCharset.get(),
00179                   nsISaveAsCharset::attr_EntityAfterCharsetConv +
00180                   nsISaveAsCharset::attr_FallbackQuestionMark,
00181                   nsIEntityConverter::transliterate);
00182   NS_ENSURE_SUCCESS(rv, rv);
00183 
00184   rv = converter->Convert(inUnicode, outPlainTextData);
00185   *outPlainTextLen = *outPlainTextData ? strlen(*outPlainTextData) : 0;
00186 
00187   NS_ASSERTION ( NS_SUCCEEDED(rv), "Error converting unicode to plain text" );
00188   
00189   return rv;
00190 } // ConvertUnicodeToPlatformPlainText
00191 
00192 
00193 //
00194 // ConvertPlatformPlainTextToUnicode
00195 //
00196 // Given a char buffer (flavor text/plaikn), this converts it to unicode using
00197 // the appropriate platform charset encoding. |outUnicode| is null terminated, 
00198 // but its length parameter, |outUnicodeLen|, does not reflect that. |outUnicodeLen| is
00199 // the length of the string in characters, not bytes.
00200 //
00201 nsresult
00202 nsPrimitiveHelpers :: ConvertPlatformPlainTextToUnicode ( const char* inText, PRInt32 inTextLen, 
00203                                                             PRUnichar** outUnicode, PRInt32* outUnicodeLen )
00204 {
00205   if ( !outUnicode || !outUnicodeLen )
00206     return NS_ERROR_INVALID_ARG;
00207 
00208   // Get the appropriate unicode decoder. We're guaranteed that this won't change
00209   // through the life of the app so we can cache it.
00210   nsresult rv;
00211   static nsCOMPtr<nsIUnicodeDecoder> decoder;
00212   static PRBool hasConverter = PR_FALSE;
00213   if ( !hasConverter ) {
00214     // get the charset
00215     nsCAutoString platformCharset;
00216     nsCOMPtr <nsIPlatformCharset> platformCharsetService = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
00217     if (NS_SUCCEEDED(rv))
00218       rv = platformCharsetService->GetCharset(kPlatformCharsetSel_PlainTextInClipboard, platformCharset);
00219     if (NS_FAILED(rv))
00220       platformCharset.AssignLiteral("ISO-8859-1");
00221       
00222     // get the decoder
00223     nsCOMPtr<nsICharsetConverterManager> ccm = 
00224              do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);  
00225     rv = ccm->GetUnicodeDecoderRaw(platformCharset.get(),
00226                                    getter_AddRefs(decoder));
00227 
00228     NS_ASSERTION(NS_SUCCEEDED(rv), "GetUnicodeEncoderRaw failed.");
00229     if (NS_FAILED(rv))
00230       return NS_ERROR_FAILURE;
00231 
00232     hasConverter = PR_TRUE;
00233   }
00234   
00235   // Estimate out length and allocate the buffer based on a worst-case estimate, then do
00236   // the conversion. 
00237   decoder->GetMaxLength(inText, inTextLen, outUnicodeLen);   // |outUnicodeLen| is number of chars
00238   if ( *outUnicodeLen ) {
00239     *outUnicode = NS_REINTERPRET_CAST(PRUnichar*, nsMemory::Alloc((*outUnicodeLen + 1) * sizeof(PRUnichar)));
00240     if ( *outUnicode ) {
00241       rv = decoder->Convert(inText, &inTextLen, *outUnicode, outUnicodeLen);
00242       (*outUnicode)[*outUnicodeLen] = '\0';                   // null terminate. Convert() doesn't do it for us
00243     }
00244   } // if valid length
00245 
00246   NS_ASSERTION ( NS_SUCCEEDED(rv), "Error converting plain text to unicode" );
00247 
00248   return rv;
00249 } // ConvertPlatformPlainTextToUnicode
00250 
00251 
00252 #ifdef XP_MAC
00253 #pragma mark -
00254 #endif
00255 
00256 
00257 //
00258 // ConvertPlatformToDOMLinebreaks
00259 //
00260 // Given some data, convert from the platform linebreaks into the LF expected by the
00261 // DOM. This will attempt to convert the data in place, but the buffer may still need to
00262 // be reallocated regardless (disposing the old buffer is taken care of internally, see
00263 // the note below).
00264 //
00265 // NOTE: this assumes that it can use nsMemory to dispose of the old buffer.
00266 //
00267 nsresult
00268 nsLinebreakHelpers :: ConvertPlatformToDOMLinebreaks ( const char* inFlavor, void** ioData, 
00269                                                           PRInt32* ioLengthInBytes )
00270 {
00271   NS_ASSERTION ( ioData && *ioData && ioLengthInBytes, "Bad Params");
00272   if ( !(ioData && *ioData && ioLengthInBytes) )
00273     return NS_ERROR_INVALID_ARG;
00274     
00275   nsresult retVal = NS_OK;
00276   
00277   if ( strcmp(inFlavor, "text/plain") == 0 ) {
00278     char* buffAsChars = NS_REINTERPRET_CAST(char*, *ioData);
00279     char* oldBuffer = buffAsChars;
00280     retVal = nsLinebreakConverter::ConvertLineBreaksInSitu ( &buffAsChars, nsLinebreakConverter::eLinebreakAny, 
00281                                                               nsLinebreakConverter::eLinebreakContent, 
00282                                                               *ioLengthInBytes, ioLengthInBytes );
00283     if ( NS_SUCCEEDED(retVal) ) {
00284       if ( buffAsChars != oldBuffer )             // check if buffer was reallocated
00285         nsMemory::Free ( oldBuffer );
00286       *ioData = buffAsChars;
00287     }
00288   }
00289   else if ( strcmp(inFlavor, "image/jpeg") == 0 ) {
00290     // I'd assume we don't want to do anything for binary data....
00291   }
00292   else {       
00293     PRUnichar* buffAsUnichar = NS_REINTERPRET_CAST(PRUnichar*, *ioData);
00294     PRUnichar* oldBuffer = buffAsUnichar;
00295     PRInt32 newLengthInChars;
00296     retVal = nsLinebreakConverter::ConvertUnicharLineBreaksInSitu ( &buffAsUnichar, nsLinebreakConverter::eLinebreakAny, 
00297                                                                      nsLinebreakConverter::eLinebreakContent, 
00298                                                                      *ioLengthInBytes / sizeof(PRUnichar), &newLengthInChars );
00299     if ( NS_SUCCEEDED(retVal) ) {
00300       if ( buffAsUnichar != oldBuffer )           // check if buffer was reallocated
00301         nsMemory::Free ( oldBuffer );
00302       *ioData = buffAsUnichar;
00303       *ioLengthInBytes = newLengthInChars * sizeof(PRUnichar);
00304     }
00305   }
00306   
00307   return retVal;
00308 
00309 } // ConvertPlatformToDOMLinebreaks