widget/xpwidgets/nsPrimitiveHelpers.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     8 //
     9 // Part of the reason these routines are all in once place is so that as new
    10 // data flavors are added that are known to be one-byte or two-byte strings, or even
    11 // raw binary data, then we just have to go to one place to change how the data
    12 // moves into/out of the primitives and native line endings.
    13 //
    14 // If you add new flavors that have special consideration (binary data or one-byte
    15 // char* strings), please update all the helper classes in this file.
    16 //
    17 // For now, this is the assumption that we are making:
    18 //  - text/plain is always a char*
    19 //  - anything else is a char16_t*
    20 //
    23 #include "nsPrimitiveHelpers.h"
    24 #include "nsCOMPtr.h"
    25 #include "nsXPCOM.h"
    26 #include "nsISupportsPrimitives.h"
    27 #include "nsITransferable.h"
    28 #include "nsIComponentManager.h"
    29 #include "nsLinebreakConverter.h"
    30 #include "nsReadableUtils.h"
    32 #include "nsIServiceManager.h"
    33 #include "nsICharsetConverterManager.h"
    34 // unicode conversion
    35 #  include "nsIPlatformCharset.h"
    36 #include "nsISaveAsCharset.h"
    37 #include "nsAutoPtr.h"
    38 #include "mozilla/Likely.h"
    41 //
    42 // CreatePrimitiveForData
    43 //
    44 // Given some data and the flavor it corresponds to, creates the appropriate
    45 // nsISupports* wrapper for passing across IDL boundaries. Right now, everything
    46 // creates a two-byte |nsISupportsString|, except for "text/plain" and native
    47 // platform HTML (CF_HTML on win32)
    48 //
    49 void
    50 nsPrimitiveHelpers :: CreatePrimitiveForData ( const char* aFlavor, const void* aDataBuff,
    51                                                  uint32_t aDataLen, nsISupports** aPrimitive )
    52 {
    53   if ( !aPrimitive )
    54     return;
    56   if ( strcmp(aFlavor,kTextMime) == 0 || strcmp(aFlavor,kNativeHTMLMime) == 0 ) {
    57     nsCOMPtr<nsISupportsCString> primitive =
    58         do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
    59     if ( primitive ) {
    60       const char * start = reinterpret_cast<const char*>(aDataBuff);
    61       primitive->SetData(Substring(start, start + aDataLen));
    62       NS_ADDREF(*aPrimitive = primitive);
    63     }
    64   }
    65   else {
    66     nsCOMPtr<nsISupportsString> primitive =
    67         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
    68     if (primitive ) {
    69       if (aDataLen % 2) { 
    70         nsAutoArrayPtr<char> buffer(new char[aDataLen + 1]);
    71         if (!MOZ_LIKELY(buffer))
    72           return;
    74         memcpy(buffer, aDataBuff, aDataLen);
    75         buffer[aDataLen] = 0;
    76         const char16_t* start = reinterpret_cast<const char16_t*>(buffer.get());
    77         // recall that length takes length as characters, not bytes
    78         primitive->SetData(Substring(start, start + (aDataLen + 1) / 2));
    79       } else {
    80         const char16_t* start = reinterpret_cast<const char16_t*>(aDataBuff);
    81         // recall that length takes length as characters, not bytes
    82         primitive->SetData(Substring(start, start + (aDataLen / 2)));
    83       }
    84       NS_ADDREF(*aPrimitive = primitive);
    85     }  
    86   }
    88 } // CreatePrimitiveForData
    91 //
    92 // CreateDataFromPrimitive
    93 //
    94 // Given a nsISupports* primitive and the flavor it represents, creates a new data
    95 // buffer with the data in it. This data will be null terminated, but the length
    96 // parameter does not reflect that.
    97 //
    98 void
    99 nsPrimitiveHelpers :: CreateDataFromPrimitive ( const char* aFlavor, nsISupports* aPrimitive, 
   100                                                    void** aDataBuff, uint32_t aDataLen )
   101 {
   102   if ( !aDataBuff )
   103     return;
   105   *aDataBuff = nullptr;
   107   if ( strcmp(aFlavor,kTextMime) == 0 ) {
   108     nsCOMPtr<nsISupportsCString> plainText ( do_QueryInterface(aPrimitive) );
   109     if ( plainText ) {
   110       nsAutoCString data;
   111       plainText->GetData ( data );
   112       *aDataBuff = ToNewCString(data);
   113     }
   114   }
   115   else {
   116     nsCOMPtr<nsISupportsString> doubleByteText ( do_QueryInterface(aPrimitive) );
   117     if ( doubleByteText ) {
   118       nsAutoString data;
   119       doubleByteText->GetData ( data );
   120       *aDataBuff = ToNewUnicode(data);
   121     }
   122   }
   124 }
   127 //
   128 // ConvertUnicodeToPlatformPlainText
   129 //
   130 // Given a unicode buffer (flavor text/unicode), this converts it to plain text using
   131 // the appropriate platform charset encoding. |inUnicodeLen| is the length of the input
   132 // string, not the # of bytes in the buffer. The |outPlainTextData| is null terminated, 
   133 // but its length parameter, |outPlainTextLen|, does not reflect that.
   134 //
   135 nsresult
   136 nsPrimitiveHelpers :: ConvertUnicodeToPlatformPlainText ( char16_t* inUnicode, int32_t inUnicodeLen, 
   137                                                             char** outPlainTextData, int32_t* outPlainTextLen )
   138 {
   139   if ( !outPlainTextData || !outPlainTextLen )
   140     return NS_ERROR_INVALID_ARG;
   142   // get the charset
   143   nsresult rv;
   144   nsCOMPtr <nsIPlatformCharset> platformCharsetService = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
   146   nsAutoCString platformCharset;
   147   if (NS_SUCCEEDED(rv))
   148     rv = platformCharsetService->GetCharset(kPlatformCharsetSel_PlainTextInClipboard, platformCharset);
   149   if (NS_FAILED(rv))
   150     platformCharset.AssignLiteral("ISO-8859-1");
   152   // use transliterate to convert things like smart quotes to normal quotes for plain text
   154   nsCOMPtr<nsISaveAsCharset> converter = do_CreateInstance("@mozilla.org/intl/saveascharset;1", &rv);
   155   NS_ENSURE_SUCCESS(rv, rv);
   157   rv = converter->Init(platformCharset.get(),
   158                   nsISaveAsCharset::attr_EntityAfterCharsetConv +
   159                   nsISaveAsCharset::attr_FallbackQuestionMark,
   160                   nsIEntityConverter::transliterate);
   161   NS_ENSURE_SUCCESS(rv, rv);
   163   rv = converter->Convert(inUnicode, outPlainTextData);
   164   *outPlainTextLen = *outPlainTextData ? strlen(*outPlainTextData) : 0;
   166   NS_ASSERTION ( NS_SUCCEEDED(rv), "Error converting unicode to plain text" );
   168   return rv;
   169 } // ConvertUnicodeToPlatformPlainText
   172 //
   173 // ConvertPlatformPlainTextToUnicode
   174 //
   175 // Given a char buffer (flavor text/plaikn), this converts it to unicode using
   176 // the appropriate platform charset encoding. |outUnicode| is null terminated, 
   177 // but its length parameter, |outUnicodeLen|, does not reflect that. |outUnicodeLen| is
   178 // the length of the string in characters, not bytes.
   179 //
   180 nsresult
   181 nsPrimitiveHelpers :: ConvertPlatformPlainTextToUnicode ( const char* inText, int32_t inTextLen, 
   182                                                             char16_t** outUnicode, int32_t* outUnicodeLen )
   183 {
   184   if ( !outUnicode || !outUnicodeLen )
   185     return NS_ERROR_INVALID_ARG;
   187   // Get the appropriate unicode decoder. We're guaranteed that this won't change
   188   // through the life of the app so we can cache it.
   189   nsresult rv = NS_OK;
   190   static nsCOMPtr<nsIUnicodeDecoder> decoder;
   191   static bool hasConverter = false;
   192   if ( !hasConverter ) {
   193     // get the charset
   194     nsAutoCString platformCharset;
   195     nsCOMPtr <nsIPlatformCharset> platformCharsetService = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
   196     if (NS_SUCCEEDED(rv))
   197       rv = platformCharsetService->GetCharset(kPlatformCharsetSel_PlainTextInClipboard, platformCharset);
   198     if (NS_FAILED(rv))
   199       platformCharset.AssignLiteral("ISO-8859-1");
   201     // get the decoder
   202     nsCOMPtr<nsICharsetConverterManager> ccm = 
   203              do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);  
   204     rv = ccm->GetUnicodeDecoderRaw(platformCharset.get(),
   205                                    getter_AddRefs(decoder));
   207     NS_ASSERTION(NS_SUCCEEDED(rv), "GetUnicodeEncoderRaw failed.");
   208     if (NS_FAILED(rv))
   209       return NS_ERROR_FAILURE;
   211     hasConverter = true;
   212   }
   214   // Estimate out length and allocate the buffer based on a worst-case estimate, then do
   215   // the conversion. 
   216   decoder->GetMaxLength(inText, inTextLen, outUnicodeLen);   // |outUnicodeLen| is number of chars
   217   if ( *outUnicodeLen ) {
   218     *outUnicode = reinterpret_cast<char16_t*>(nsMemory::Alloc((*outUnicodeLen + 1) * sizeof(char16_t)));
   219     if ( *outUnicode ) {
   220       rv = decoder->Convert(inText, &inTextLen, *outUnicode, outUnicodeLen);
   221       (*outUnicode)[*outUnicodeLen] = '\0';                   // null terminate. Convert() doesn't do it for us
   222     }
   223   } // if valid length
   225   NS_ASSERTION ( NS_SUCCEEDED(rv), "Error converting plain text to unicode" );
   227   return rv;
   228 } // ConvertPlatformPlainTextToUnicode
   231 //
   232 // ConvertPlatformToDOMLinebreaks
   233 //
   234 // Given some data, convert from the platform linebreaks into the LF expected by the
   235 // DOM. This will attempt to convert the data in place, but the buffer may still need to
   236 // be reallocated regardless (disposing the old buffer is taken care of internally, see
   237 // the note below).
   238 //
   239 // NOTE: this assumes that it can use nsMemory to dispose of the old buffer.
   240 //
   241 nsresult
   242 nsLinebreakHelpers :: ConvertPlatformToDOMLinebreaks ( const char* inFlavor, void** ioData, 
   243                                                           int32_t* ioLengthInBytes )
   244 {
   245   NS_ASSERTION ( ioData && *ioData && ioLengthInBytes, "Bad Params");
   246   if ( !(ioData && *ioData && ioLengthInBytes) )
   247     return NS_ERROR_INVALID_ARG;
   249   nsresult retVal = NS_OK;
   251   if ( strcmp(inFlavor, "text/plain") == 0 ) {
   252     char* buffAsChars = reinterpret_cast<char*>(*ioData);
   253     char* oldBuffer = buffAsChars;
   254     retVal = nsLinebreakConverter::ConvertLineBreaksInSitu ( &buffAsChars, nsLinebreakConverter::eLinebreakAny, 
   255                                                               nsLinebreakConverter::eLinebreakContent, 
   256                                                               *ioLengthInBytes, ioLengthInBytes );
   257     if ( NS_SUCCEEDED(retVal) ) {
   258       if ( buffAsChars != oldBuffer )             // check if buffer was reallocated
   259         nsMemory::Free ( oldBuffer );
   260       *ioData = buffAsChars;
   261     }
   262   }
   263   else if ( strcmp(inFlavor, "image/jpeg") == 0 ) {
   264     // I'd assume we don't want to do anything for binary data....
   265   }
   266   else {       
   267     char16_t* buffAsUnichar = reinterpret_cast<char16_t*>(*ioData);
   268     char16_t* oldBuffer = buffAsUnichar;
   269     int32_t newLengthInChars;
   270     retVal = nsLinebreakConverter::ConvertUnicharLineBreaksInSitu ( &buffAsUnichar, nsLinebreakConverter::eLinebreakAny, 
   271                                                                      nsLinebreakConverter::eLinebreakContent, 
   272                                                                      *ioLengthInBytes / sizeof(char16_t), &newLengthInChars );
   273     if ( NS_SUCCEEDED(retVal) ) {
   274       if ( buffAsUnichar != oldBuffer )           // check if buffer was reallocated
   275         nsMemory::Free ( oldBuffer );
   276       *ioData = buffAsUnichar;
   277       *ioLengthInBytes = newLengthInChars * sizeof(char16_t);
   278     }
   279   }
   281   return retVal;
   283 } // ConvertPlatformToDOMLinebreaks

mercurial