1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/xpwidgets/nsPrimitiveHelpers.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,283 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 + 1.11 +// 1.12 +// Part of the reason these routines are all in once place is so that as new 1.13 +// data flavors are added that are known to be one-byte or two-byte strings, or even 1.14 +// raw binary data, then we just have to go to one place to change how the data 1.15 +// moves into/out of the primitives and native line endings. 1.16 +// 1.17 +// If you add new flavors that have special consideration (binary data or one-byte 1.18 +// char* strings), please update all the helper classes in this file. 1.19 +// 1.20 +// For now, this is the assumption that we are making: 1.21 +// - text/plain is always a char* 1.22 +// - anything else is a char16_t* 1.23 +// 1.24 + 1.25 + 1.26 +#include "nsPrimitiveHelpers.h" 1.27 +#include "nsCOMPtr.h" 1.28 +#include "nsXPCOM.h" 1.29 +#include "nsISupportsPrimitives.h" 1.30 +#include "nsITransferable.h" 1.31 +#include "nsIComponentManager.h" 1.32 +#include "nsLinebreakConverter.h" 1.33 +#include "nsReadableUtils.h" 1.34 + 1.35 +#include "nsIServiceManager.h" 1.36 +#include "nsICharsetConverterManager.h" 1.37 +// unicode conversion 1.38 +# include "nsIPlatformCharset.h" 1.39 +#include "nsISaveAsCharset.h" 1.40 +#include "nsAutoPtr.h" 1.41 +#include "mozilla/Likely.h" 1.42 + 1.43 + 1.44 +// 1.45 +// CreatePrimitiveForData 1.46 +// 1.47 +// Given some data and the flavor it corresponds to, creates the appropriate 1.48 +// nsISupports* wrapper for passing across IDL boundaries. Right now, everything 1.49 +// creates a two-byte |nsISupportsString|, except for "text/plain" and native 1.50 +// platform HTML (CF_HTML on win32) 1.51 +// 1.52 +void 1.53 +nsPrimitiveHelpers :: CreatePrimitiveForData ( const char* aFlavor, const void* aDataBuff, 1.54 + uint32_t aDataLen, nsISupports** aPrimitive ) 1.55 +{ 1.56 + if ( !aPrimitive ) 1.57 + return; 1.58 + 1.59 + if ( strcmp(aFlavor,kTextMime) == 0 || strcmp(aFlavor,kNativeHTMLMime) == 0 ) { 1.60 + nsCOMPtr<nsISupportsCString> primitive = 1.61 + do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID); 1.62 + if ( primitive ) { 1.63 + const char * start = reinterpret_cast<const char*>(aDataBuff); 1.64 + primitive->SetData(Substring(start, start + aDataLen)); 1.65 + NS_ADDREF(*aPrimitive = primitive); 1.66 + } 1.67 + } 1.68 + else { 1.69 + nsCOMPtr<nsISupportsString> primitive = 1.70 + do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); 1.71 + if (primitive ) { 1.72 + if (aDataLen % 2) { 1.73 + nsAutoArrayPtr<char> buffer(new char[aDataLen + 1]); 1.74 + if (!MOZ_LIKELY(buffer)) 1.75 + return; 1.76 + 1.77 + memcpy(buffer, aDataBuff, aDataLen); 1.78 + buffer[aDataLen] = 0; 1.79 + const char16_t* start = reinterpret_cast<const char16_t*>(buffer.get()); 1.80 + // recall that length takes length as characters, not bytes 1.81 + primitive->SetData(Substring(start, start + (aDataLen + 1) / 2)); 1.82 + } else { 1.83 + const char16_t* start = reinterpret_cast<const char16_t*>(aDataBuff); 1.84 + // recall that length takes length as characters, not bytes 1.85 + primitive->SetData(Substring(start, start + (aDataLen / 2))); 1.86 + } 1.87 + NS_ADDREF(*aPrimitive = primitive); 1.88 + } 1.89 + } 1.90 + 1.91 +} // CreatePrimitiveForData 1.92 + 1.93 + 1.94 +// 1.95 +// CreateDataFromPrimitive 1.96 +// 1.97 +// Given a nsISupports* primitive and the flavor it represents, creates a new data 1.98 +// buffer with the data in it. This data will be null terminated, but the length 1.99 +// parameter does not reflect that. 1.100 +// 1.101 +void 1.102 +nsPrimitiveHelpers :: CreateDataFromPrimitive ( const char* aFlavor, nsISupports* aPrimitive, 1.103 + void** aDataBuff, uint32_t aDataLen ) 1.104 +{ 1.105 + if ( !aDataBuff ) 1.106 + return; 1.107 + 1.108 + *aDataBuff = nullptr; 1.109 + 1.110 + if ( strcmp(aFlavor,kTextMime) == 0 ) { 1.111 + nsCOMPtr<nsISupportsCString> plainText ( do_QueryInterface(aPrimitive) ); 1.112 + if ( plainText ) { 1.113 + nsAutoCString data; 1.114 + plainText->GetData ( data ); 1.115 + *aDataBuff = ToNewCString(data); 1.116 + } 1.117 + } 1.118 + else { 1.119 + nsCOMPtr<nsISupportsString> doubleByteText ( do_QueryInterface(aPrimitive) ); 1.120 + if ( doubleByteText ) { 1.121 + nsAutoString data; 1.122 + doubleByteText->GetData ( data ); 1.123 + *aDataBuff = ToNewUnicode(data); 1.124 + } 1.125 + } 1.126 + 1.127 +} 1.128 + 1.129 + 1.130 +// 1.131 +// ConvertUnicodeToPlatformPlainText 1.132 +// 1.133 +// Given a unicode buffer (flavor text/unicode), this converts it to plain text using 1.134 +// the appropriate platform charset encoding. |inUnicodeLen| is the length of the input 1.135 +// string, not the # of bytes in the buffer. The |outPlainTextData| is null terminated, 1.136 +// but its length parameter, |outPlainTextLen|, does not reflect that. 1.137 +// 1.138 +nsresult 1.139 +nsPrimitiveHelpers :: ConvertUnicodeToPlatformPlainText ( char16_t* inUnicode, int32_t inUnicodeLen, 1.140 + char** outPlainTextData, int32_t* outPlainTextLen ) 1.141 +{ 1.142 + if ( !outPlainTextData || !outPlainTextLen ) 1.143 + return NS_ERROR_INVALID_ARG; 1.144 + 1.145 + // get the charset 1.146 + nsresult rv; 1.147 + nsCOMPtr <nsIPlatformCharset> platformCharsetService = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv); 1.148 + 1.149 + nsAutoCString platformCharset; 1.150 + if (NS_SUCCEEDED(rv)) 1.151 + rv = platformCharsetService->GetCharset(kPlatformCharsetSel_PlainTextInClipboard, platformCharset); 1.152 + if (NS_FAILED(rv)) 1.153 + platformCharset.AssignLiteral("ISO-8859-1"); 1.154 + 1.155 + // use transliterate to convert things like smart quotes to normal quotes for plain text 1.156 + 1.157 + nsCOMPtr<nsISaveAsCharset> converter = do_CreateInstance("@mozilla.org/intl/saveascharset;1", &rv); 1.158 + NS_ENSURE_SUCCESS(rv, rv); 1.159 + 1.160 + rv = converter->Init(platformCharset.get(), 1.161 + nsISaveAsCharset::attr_EntityAfterCharsetConv + 1.162 + nsISaveAsCharset::attr_FallbackQuestionMark, 1.163 + nsIEntityConverter::transliterate); 1.164 + NS_ENSURE_SUCCESS(rv, rv); 1.165 + 1.166 + rv = converter->Convert(inUnicode, outPlainTextData); 1.167 + *outPlainTextLen = *outPlainTextData ? strlen(*outPlainTextData) : 0; 1.168 + 1.169 + NS_ASSERTION ( NS_SUCCEEDED(rv), "Error converting unicode to plain text" ); 1.170 + 1.171 + return rv; 1.172 +} // ConvertUnicodeToPlatformPlainText 1.173 + 1.174 + 1.175 +// 1.176 +// ConvertPlatformPlainTextToUnicode 1.177 +// 1.178 +// Given a char buffer (flavor text/plaikn), this converts it to unicode using 1.179 +// the appropriate platform charset encoding. |outUnicode| is null terminated, 1.180 +// but its length parameter, |outUnicodeLen|, does not reflect that. |outUnicodeLen| is 1.181 +// the length of the string in characters, not bytes. 1.182 +// 1.183 +nsresult 1.184 +nsPrimitiveHelpers :: ConvertPlatformPlainTextToUnicode ( const char* inText, int32_t inTextLen, 1.185 + char16_t** outUnicode, int32_t* outUnicodeLen ) 1.186 +{ 1.187 + if ( !outUnicode || !outUnicodeLen ) 1.188 + return NS_ERROR_INVALID_ARG; 1.189 + 1.190 + // Get the appropriate unicode decoder. We're guaranteed that this won't change 1.191 + // through the life of the app so we can cache it. 1.192 + nsresult rv = NS_OK; 1.193 + static nsCOMPtr<nsIUnicodeDecoder> decoder; 1.194 + static bool hasConverter = false; 1.195 + if ( !hasConverter ) { 1.196 + // get the charset 1.197 + nsAutoCString platformCharset; 1.198 + nsCOMPtr <nsIPlatformCharset> platformCharsetService = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv); 1.199 + if (NS_SUCCEEDED(rv)) 1.200 + rv = platformCharsetService->GetCharset(kPlatformCharsetSel_PlainTextInClipboard, platformCharset); 1.201 + if (NS_FAILED(rv)) 1.202 + platformCharset.AssignLiteral("ISO-8859-1"); 1.203 + 1.204 + // get the decoder 1.205 + nsCOMPtr<nsICharsetConverterManager> ccm = 1.206 + do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); 1.207 + rv = ccm->GetUnicodeDecoderRaw(platformCharset.get(), 1.208 + getter_AddRefs(decoder)); 1.209 + 1.210 + NS_ASSERTION(NS_SUCCEEDED(rv), "GetUnicodeEncoderRaw failed."); 1.211 + if (NS_FAILED(rv)) 1.212 + return NS_ERROR_FAILURE; 1.213 + 1.214 + hasConverter = true; 1.215 + } 1.216 + 1.217 + // Estimate out length and allocate the buffer based on a worst-case estimate, then do 1.218 + // the conversion. 1.219 + decoder->GetMaxLength(inText, inTextLen, outUnicodeLen); // |outUnicodeLen| is number of chars 1.220 + if ( *outUnicodeLen ) { 1.221 + *outUnicode = reinterpret_cast<char16_t*>(nsMemory::Alloc((*outUnicodeLen + 1) * sizeof(char16_t))); 1.222 + if ( *outUnicode ) { 1.223 + rv = decoder->Convert(inText, &inTextLen, *outUnicode, outUnicodeLen); 1.224 + (*outUnicode)[*outUnicodeLen] = '\0'; // null terminate. Convert() doesn't do it for us 1.225 + } 1.226 + } // if valid length 1.227 + 1.228 + NS_ASSERTION ( NS_SUCCEEDED(rv), "Error converting plain text to unicode" ); 1.229 + 1.230 + return rv; 1.231 +} // ConvertPlatformPlainTextToUnicode 1.232 + 1.233 + 1.234 +// 1.235 +// ConvertPlatformToDOMLinebreaks 1.236 +// 1.237 +// Given some data, convert from the platform linebreaks into the LF expected by the 1.238 +// DOM. This will attempt to convert the data in place, but the buffer may still need to 1.239 +// be reallocated regardless (disposing the old buffer is taken care of internally, see 1.240 +// the note below). 1.241 +// 1.242 +// NOTE: this assumes that it can use nsMemory to dispose of the old buffer. 1.243 +// 1.244 +nsresult 1.245 +nsLinebreakHelpers :: ConvertPlatformToDOMLinebreaks ( const char* inFlavor, void** ioData, 1.246 + int32_t* ioLengthInBytes ) 1.247 +{ 1.248 + NS_ASSERTION ( ioData && *ioData && ioLengthInBytes, "Bad Params"); 1.249 + if ( !(ioData && *ioData && ioLengthInBytes) ) 1.250 + return NS_ERROR_INVALID_ARG; 1.251 + 1.252 + nsresult retVal = NS_OK; 1.253 + 1.254 + if ( strcmp(inFlavor, "text/plain") == 0 ) { 1.255 + char* buffAsChars = reinterpret_cast<char*>(*ioData); 1.256 + char* oldBuffer = buffAsChars; 1.257 + retVal = nsLinebreakConverter::ConvertLineBreaksInSitu ( &buffAsChars, nsLinebreakConverter::eLinebreakAny, 1.258 + nsLinebreakConverter::eLinebreakContent, 1.259 + *ioLengthInBytes, ioLengthInBytes ); 1.260 + if ( NS_SUCCEEDED(retVal) ) { 1.261 + if ( buffAsChars != oldBuffer ) // check if buffer was reallocated 1.262 + nsMemory::Free ( oldBuffer ); 1.263 + *ioData = buffAsChars; 1.264 + } 1.265 + } 1.266 + else if ( strcmp(inFlavor, "image/jpeg") == 0 ) { 1.267 + // I'd assume we don't want to do anything for binary data.... 1.268 + } 1.269 + else { 1.270 + char16_t* buffAsUnichar = reinterpret_cast<char16_t*>(*ioData); 1.271 + char16_t* oldBuffer = buffAsUnichar; 1.272 + int32_t newLengthInChars; 1.273 + retVal = nsLinebreakConverter::ConvertUnicharLineBreaksInSitu ( &buffAsUnichar, nsLinebreakConverter::eLinebreakAny, 1.274 + nsLinebreakConverter::eLinebreakContent, 1.275 + *ioLengthInBytes / sizeof(char16_t), &newLengthInChars ); 1.276 + if ( NS_SUCCEEDED(retVal) ) { 1.277 + if ( buffAsUnichar != oldBuffer ) // check if buffer was reallocated 1.278 + nsMemory::Free ( oldBuffer ); 1.279 + *ioData = buffAsUnichar; 1.280 + *ioLengthInBytes = newLengthInChars * sizeof(char16_t); 1.281 + } 1.282 + } 1.283 + 1.284 + return retVal; 1.285 + 1.286 +} // ConvertPlatformToDOMLinebreaks