|
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/. */ |
|
6 |
|
7 |
|
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 // |
|
21 |
|
22 |
|
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" |
|
31 |
|
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" |
|
39 |
|
40 |
|
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; |
|
55 |
|
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; |
|
73 |
|
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 } |
|
87 |
|
88 } // CreatePrimitiveForData |
|
89 |
|
90 |
|
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; |
|
104 |
|
105 *aDataBuff = nullptr; |
|
106 |
|
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 } |
|
123 |
|
124 } |
|
125 |
|
126 |
|
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; |
|
141 |
|
142 // get the charset |
|
143 nsresult rv; |
|
144 nsCOMPtr <nsIPlatformCharset> platformCharsetService = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv); |
|
145 |
|
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"); |
|
151 |
|
152 // use transliterate to convert things like smart quotes to normal quotes for plain text |
|
153 |
|
154 nsCOMPtr<nsISaveAsCharset> converter = do_CreateInstance("@mozilla.org/intl/saveascharset;1", &rv); |
|
155 NS_ENSURE_SUCCESS(rv, rv); |
|
156 |
|
157 rv = converter->Init(platformCharset.get(), |
|
158 nsISaveAsCharset::attr_EntityAfterCharsetConv + |
|
159 nsISaveAsCharset::attr_FallbackQuestionMark, |
|
160 nsIEntityConverter::transliterate); |
|
161 NS_ENSURE_SUCCESS(rv, rv); |
|
162 |
|
163 rv = converter->Convert(inUnicode, outPlainTextData); |
|
164 *outPlainTextLen = *outPlainTextData ? strlen(*outPlainTextData) : 0; |
|
165 |
|
166 NS_ASSERTION ( NS_SUCCEEDED(rv), "Error converting unicode to plain text" ); |
|
167 |
|
168 return rv; |
|
169 } // ConvertUnicodeToPlatformPlainText |
|
170 |
|
171 |
|
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; |
|
186 |
|
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"); |
|
200 |
|
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)); |
|
206 |
|
207 NS_ASSERTION(NS_SUCCEEDED(rv), "GetUnicodeEncoderRaw failed."); |
|
208 if (NS_FAILED(rv)) |
|
209 return NS_ERROR_FAILURE; |
|
210 |
|
211 hasConverter = true; |
|
212 } |
|
213 |
|
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 |
|
224 |
|
225 NS_ASSERTION ( NS_SUCCEEDED(rv), "Error converting plain text to unicode" ); |
|
226 |
|
227 return rv; |
|
228 } // ConvertPlatformPlainTextToUnicode |
|
229 |
|
230 |
|
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; |
|
248 |
|
249 nsresult retVal = NS_OK; |
|
250 |
|
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 } |
|
280 |
|
281 return retVal; |
|
282 |
|
283 } // ConvertPlatformToDOMLinebreaks |