Fri, 16 Jan 2015 04:50:19 +0100
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