1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
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.
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.
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*
23 #include "nsPrimitiveHelpers.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"
34 #include "nsIPlatformCharset.h"
35 #include "nsIUnicodeDecoder.h"
36 #include "nsISaveAsCharset.h"
37 #include "nsAutoPtr.h"
38 #include "mozilla/Likely.h"
39 #include "mozilla/dom/EncodingUtils.h"
41 using mozilla::dom::EncodingUtils
;
45 // CreatePrimitiveForData
47 // Given some data and the flavor it corresponds to, creates the appropriate
48 // nsISupports* wrapper for passing across IDL boundaries. Right now, everything
49 // creates a two-byte |nsISupportsString|, except for "text/plain" and native
50 // platform HTML (CF_HTML on win32)
53 nsPrimitiveHelpers :: CreatePrimitiveForData ( const char* aFlavor
, const void* aDataBuff
,
54 uint32_t aDataLen
, nsISupports
** aPrimitive
)
59 if ( strcmp(aFlavor
,kTextMime
) == 0 || strcmp(aFlavor
,kNativeHTMLMime
) == 0 ) {
60 nsCOMPtr
<nsISupportsCString
> primitive
=
61 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID
);
63 const char * start
= reinterpret_cast<const char*>(aDataBuff
);
64 primitive
->SetData(Substring(start
, start
+ aDataLen
));
65 NS_ADDREF(*aPrimitive
= primitive
);
69 nsCOMPtr
<nsISupportsString
> primitive
=
70 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
);
73 nsAutoArrayPtr
<char> buffer(new char[aDataLen
+ 1]);
74 if (!MOZ_LIKELY(buffer
))
77 memcpy(buffer
, aDataBuff
, aDataLen
);
79 const char16_t
* start
= reinterpret_cast<const char16_t
*>(buffer
.get());
80 // recall that length takes length as characters, not bytes
81 primitive
->SetData(Substring(start
, start
+ (aDataLen
+ 1) / 2));
83 const char16_t
* start
= reinterpret_cast<const char16_t
*>(aDataBuff
);
84 // recall that length takes length as characters, not bytes
85 primitive
->SetData(Substring(start
, start
+ (aDataLen
/ 2)));
87 NS_ADDREF(*aPrimitive
= primitive
);
91 } // CreatePrimitiveForData
95 // CreateDataFromPrimitive
97 // Given a nsISupports* primitive and the flavor it represents, creates a new data
98 // buffer with the data in it. This data will be null terminated, but the length
99 // parameter does not reflect that.
102 nsPrimitiveHelpers :: CreateDataFromPrimitive ( const char* aFlavor
, nsISupports
* aPrimitive
,
103 void** aDataBuff
, uint32_t aDataLen
)
108 *aDataBuff
= nullptr;
110 if ( strcmp(aFlavor
,kTextMime
) == 0 ) {
111 nsCOMPtr
<nsISupportsCString
> plainText ( do_QueryInterface(aPrimitive
) );
114 plainText
->GetData ( data
);
115 *aDataBuff
= ToNewCString(data
);
119 nsCOMPtr
<nsISupportsString
> doubleByteText ( do_QueryInterface(aPrimitive
) );
120 if ( doubleByteText
) {
122 doubleByteText
->GetData ( data
);
123 *aDataBuff
= ToNewUnicode(data
);
131 // ConvertUnicodeToPlatformPlainText
133 // Given a unicode buffer (flavor text/unicode), this converts it to plain text using
134 // the appropriate platform charset encoding. |inUnicodeLen| is the length of the input
135 // string, not the # of bytes in the buffer. The |outPlainTextData| is null terminated,
136 // but its length parameter, |outPlainTextLen|, does not reflect that.
139 nsPrimitiveHelpers :: ConvertUnicodeToPlatformPlainText ( char16_t
* inUnicode
, int32_t inUnicodeLen
,
140 char** outPlainTextData
, int32_t* outPlainTextLen
)
142 if ( !outPlainTextData
|| !outPlainTextLen
)
143 return NS_ERROR_INVALID_ARG
;
147 nsCOMPtr
<nsIPlatformCharset
> platformCharsetService
= do_GetService(NS_PLATFORMCHARSET_CONTRACTID
, &rv
);
149 nsAutoCString platformCharset
;
150 if (NS_SUCCEEDED(rv
))
151 rv
= platformCharsetService
->GetCharset(kPlatformCharsetSel_PlainTextInClipboard
, platformCharset
);
153 platformCharset
.AssignLiteral("ISO-8859-1");
155 // use transliterate to convert things like smart quotes to normal quotes for plain text
157 nsCOMPtr
<nsISaveAsCharset
> converter
= do_CreateInstance("@mozilla.org/intl/saveascharset;1", &rv
);
158 NS_ENSURE_SUCCESS(rv
, rv
);
160 rv
= converter
->Init(platformCharset
.get(),
161 nsISaveAsCharset::attr_EntityAfterCharsetConv
+
162 nsISaveAsCharset::attr_FallbackQuestionMark
,
163 nsIEntityConverter::transliterate
);
164 NS_ENSURE_SUCCESS(rv
, rv
);
166 rv
= converter
->Convert(inUnicode
, outPlainTextData
);
167 *outPlainTextLen
= *outPlainTextData
? strlen(*outPlainTextData
) : 0;
169 NS_ASSERTION ( NS_SUCCEEDED(rv
), "Error converting unicode to plain text" );
172 } // ConvertUnicodeToPlatformPlainText
176 // ConvertPlatformPlainTextToUnicode
178 // Given a char buffer (flavor text/plaikn), this converts it to unicode using
179 // the appropriate platform charset encoding. |outUnicode| is null terminated,
180 // but its length parameter, |outUnicodeLen|, does not reflect that. |outUnicodeLen| is
181 // the length of the string in characters, not bytes.
184 nsPrimitiveHelpers :: ConvertPlatformPlainTextToUnicode ( const char* inText
, int32_t inTextLen
,
185 char16_t
** outUnicode
, int32_t* outUnicodeLen
)
187 if ( !outUnicode
|| !outUnicodeLen
)
188 return NS_ERROR_INVALID_ARG
;
190 // Get the appropriate unicode decoder. We're guaranteed that this won't change
191 // through the life of the app so we can cache it.
193 static nsCOMPtr
<nsIUnicodeDecoder
> decoder
;
194 static bool hasConverter
= false;
195 if ( !hasConverter
) {
197 nsAutoCString platformCharset
;
198 nsCOMPtr
<nsIPlatformCharset
> platformCharsetService
= do_GetService(NS_PLATFORMCHARSET_CONTRACTID
, &rv
);
199 if (NS_SUCCEEDED(rv
))
200 rv
= platformCharsetService
->GetCharset(kPlatformCharsetSel_PlainTextInClipboard
, platformCharset
);
202 platformCharset
.AssignLiteral("windows-1252");
204 decoder
= EncodingUtils::DecoderForEncoding(platformCharset
);
209 // Estimate out length and allocate the buffer based on a worst-case estimate, then do
211 decoder
->GetMaxLength(inText
, inTextLen
, outUnicodeLen
); // |outUnicodeLen| is number of chars
212 if ( *outUnicodeLen
) {
213 *outUnicode
= reinterpret_cast<char16_t
*>(nsMemory::Alloc((*outUnicodeLen
+ 1) * sizeof(char16_t
)));
215 rv
= decoder
->Convert(inText
, &inTextLen
, *outUnicode
, outUnicodeLen
);
216 (*outUnicode
)[*outUnicodeLen
] = '\0'; // null terminate. Convert() doesn't do it for us
220 NS_ASSERTION ( NS_SUCCEEDED(rv
), "Error converting plain text to unicode" );
223 } // ConvertPlatformPlainTextToUnicode
227 // ConvertPlatformToDOMLinebreaks
229 // Given some data, convert from the platform linebreaks into the LF expected by the
230 // DOM. This will attempt to convert the data in place, but the buffer may still need to
231 // be reallocated regardless (disposing the old buffer is taken care of internally, see
234 // NOTE: this assumes that it can use nsMemory to dispose of the old buffer.
237 nsLinebreakHelpers :: ConvertPlatformToDOMLinebreaks ( const char* inFlavor
, void** ioData
,
238 int32_t* ioLengthInBytes
)
240 NS_ASSERTION ( ioData
&& *ioData
&& ioLengthInBytes
, "Bad Params");
241 if ( !(ioData
&& *ioData
&& ioLengthInBytes
) )
242 return NS_ERROR_INVALID_ARG
;
244 nsresult retVal
= NS_OK
;
246 if ( strcmp(inFlavor
, "text/plain") == 0 ) {
247 char* buffAsChars
= reinterpret_cast<char*>(*ioData
);
248 char* oldBuffer
= buffAsChars
;
249 retVal
= nsLinebreakConverter::ConvertLineBreaksInSitu ( &buffAsChars
, nsLinebreakConverter::eLinebreakAny
,
250 nsLinebreakConverter::eLinebreakContent
,
251 *ioLengthInBytes
, ioLengthInBytes
);
252 if ( NS_SUCCEEDED(retVal
) ) {
253 if ( buffAsChars
!= oldBuffer
) // check if buffer was reallocated
254 nsMemory::Free ( oldBuffer
);
255 *ioData
= buffAsChars
;
258 else if ( strcmp(inFlavor
, "image/jpeg") == 0 ) {
259 // I'd assume we don't want to do anything for binary data....
262 char16_t
* buffAsUnichar
= reinterpret_cast<char16_t
*>(*ioData
);
263 char16_t
* oldBuffer
= buffAsUnichar
;
264 int32_t newLengthInChars
;
265 retVal
= nsLinebreakConverter::ConvertUnicharLineBreaksInSitu ( &buffAsUnichar
, nsLinebreakConverter::eLinebreakAny
,
266 nsLinebreakConverter::eLinebreakContent
,
267 *ioLengthInBytes
/ sizeof(char16_t
), &newLengthInChars
);
268 if ( NS_SUCCEEDED(retVal
) ) {
269 if ( buffAsUnichar
!= oldBuffer
) // check if buffer was reallocated
270 nsMemory::Free ( oldBuffer
);
271 *ioData
= buffAsUnichar
;
272 *ioLengthInBytes
= newLengthInChars
* sizeof(char16_t
);
278 } // ConvertPlatformToDOMLinebreaks