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