widget/xpwidgets/nsPrimitiveHelpers.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial