1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : * John Bandhauer <jband@netscape.com> (original author)
27 : * Pierre Phaneuf <pp@ludusdesign.com>
28 : * Mike Shaver <shaver@mozilla.org>
29 : *
30 : * Alternatively, the contents of this file may be used under the terms of
31 : * either of the GNU General Public License Version 2 or later (the "GPL"),
32 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 : * in which case the provisions of the GPL or the LGPL are applicable instead
34 : * of those above. If you wish to allow use of your version of this file only
35 : * under the terms of either the GPL or the LGPL, and not to allow others to
36 : * use your version of this file under the terms of the MPL, indicate your
37 : * decision by deleting the provisions above and replace them with the notice
38 : * and other provisions required by the GPL or the LGPL. If you do not delete
39 : * the provisions above, a recipient may use your version of this file under
40 : * the terms of any one of the MPL, the GPL or the LGPL.
41 : *
42 : * ***** END LICENSE BLOCK ***** */
43 :
44 : /* Data conversion between native and JavaScript types. */
45 :
46 : #include "mozilla/Util.h"
47 :
48 : #include "xpcprivate.h"
49 : #include "nsString.h"
50 : #include "nsIAtom.h"
51 : #include "XPCWrapper.h"
52 : #include "nsJSPrincipals.h"
53 : #include "nsWrapperCache.h"
54 : #include "WrapperFactory.h"
55 : #include "AccessCheck.h"
56 : #include "nsJSUtils.h"
57 :
58 : #include "dombindings.h"
59 : #include "nsWrapperCacheInlines.h"
60 :
61 : #include "jsapi.h"
62 : #include "jsfriendapi.h"
63 : #include "jstypedarray.h"
64 :
65 : using namespace mozilla;
66 :
67 : //#define STRICT_CHECK_OF_UNICODE
68 : #ifdef STRICT_CHECK_OF_UNICODE
69 : #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF80))
70 : #else // STRICT_CHECK_OF_UNICODE
71 : #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF00))
72 : #endif // STRICT_CHECK_OF_UNICODE
73 :
74 : #define ILLEGAL_CHAR_RANGE(c) (0!=((c) & 0x80))
75 :
76 : /***********************************************************/
77 :
78 : // static
79 : JSBool
80 1780915 : XPCConvert::IsMethodReflectable(const XPTMethodDescriptor& info)
81 : {
82 1780915 : if (XPT_MD_IS_NOTXPCOM(info.flags) || XPT_MD_IS_HIDDEN(info.flags))
83 177185 : return false;
84 :
85 3981905 : for (int i = info.num_args-1; i >= 0; i--) {
86 2378267 : const nsXPTParamInfo& param = info.params[i];
87 2378267 : const nsXPTType& type = param.GetType();
88 :
89 : // Reflected methods can't use native types. All native types end up
90 : // getting tagged as void*, so this check is easy.
91 2378267 : if (type.TagPart() == nsXPTType::T_VOID)
92 92 : return false;
93 : }
94 1603638 : return true;
95 : }
96 :
97 : /***************************************************************************/
98 :
99 : // static
100 : JSBool
101 147308 : XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface)
102 : {
103 147308 : JSClass* jsclass = js::GetObjectJSClass(obj);
104 147308 : NS_ASSERTION(jsclass, "obj has no class");
105 147308 : if (jsclass &&
106 : (jsclass->flags & JSCLASS_HAS_PRIVATE) &&
107 : (jsclass->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
108 415 : *iface = (nsISupports*) xpc_GetJSPrivate(obj);
109 415 : return true;
110 : }
111 146893 : return false;
112 : }
113 :
114 : /***************************************************************************/
115 :
116 : static void
117 871361 : FinalizeXPCOMUCString(const JSStringFinalizer *fin, jschar *chars)
118 : {
119 871361 : nsMemory::Free(chars);
120 871361 : }
121 :
122 : static const JSStringFinalizer sXPCOMUCStringFinalizer = { FinalizeXPCOMUCString };
123 :
124 : // static
125 : JSBool
126 20388463 : XPCConvert::NativeData2JS(XPCLazyCallContext& lccx, jsval* d, const void* s,
127 : const nsXPTType& type, const nsID* iid, nsresult* pErr)
128 : {
129 20388463 : NS_PRECONDITION(s, "bad param");
130 20388463 : NS_PRECONDITION(d, "bad param");
131 :
132 20388463 : JSContext* cx = lccx.GetJSContext();
133 :
134 : // Allow wrong compartment or unset ScopeForNewObject when the caller knows
135 : // the value is primitive (viz., XPCNativeMember::GetConstantValue).
136 20388463 : NS_ABORT_IF_FALSE(type.IsArithmetic() ||
137 : js::IsObjectInContextCompartment(lccx.GetScopeForNewJSObjects(), cx),
138 : "bad scope for new JSObjects");
139 :
140 20388463 : if (pErr)
141 19663070 : *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
142 :
143 20388463 : switch (type.TagPart()) {
144 0 : case nsXPTType::T_I8 : *d = INT_TO_JSVAL(int32_t(*((int8_t*)s))); break;
145 3553 : case nsXPTType::T_I16 : *d = INT_TO_JSVAL(int32_t(*((int16_t*)s))); break;
146 244194 : case nsXPTType::T_I32 : *d = INT_TO_JSVAL(*((int32_t*)s)); break;
147 125097 : case nsXPTType::T_I64 : *d = DOUBLE_TO_JSVAL(double(*((int64_t*)s))); break;
148 13479284 : case nsXPTType::T_U8 : *d = INT_TO_JSVAL(int32_t(*((uint8_t*)s))); break;
149 56934 : case nsXPTType::T_U16 : *d = INT_TO_JSVAL(int32_t(*((uint16_t*)s))); break;
150 569778 : case nsXPTType::T_U32 : *d = UINT_TO_JSVAL(*((uint32_t*)s)); break;
151 21946 : case nsXPTType::T_U64 : *d = DOUBLE_TO_JSVAL(double(*((uint64_t*)s))); break;
152 25 : case nsXPTType::T_FLOAT : *d = DOUBLE_TO_JSVAL(*((float*)s)); break;
153 52 : case nsXPTType::T_DOUBLE: *d = DOUBLE_TO_JSVAL(*((double*)s)); break;
154 : case nsXPTType::T_BOOL :
155 : {
156 1310981 : bool b = *((bool*)s);
157 :
158 : NS_WARN_IF_FALSE(b == 1 || b == 0,
159 : "Passing a malformed bool through XPConnect");
160 1310981 : *d = BOOLEAN_TO_JSVAL(!!b);
161 1310981 : break;
162 : }
163 : case nsXPTType::T_CHAR :
164 : {
165 13 : char* p = (char*)s;
166 13 : if (!p)
167 0 : return false;
168 :
169 : #ifdef STRICT_CHECK_OF_UNICODE
170 : NS_ASSERTION(! ILLEGAL_CHAR_RANGE(p) , "passing non ASCII data");
171 : #endif // STRICT_CHECK_OF_UNICODE
172 :
173 : JSString* str;
174 13 : if (!(str = JS_NewStringCopyN(cx, p, 1)))
175 0 : return false;
176 13 : *d = STRING_TO_JSVAL(str);
177 13 : break;
178 : }
179 : case nsXPTType::T_WCHAR :
180 : {
181 6 : jschar* p = (jschar*)s;
182 6 : if (!p)
183 0 : return false;
184 : JSString* str;
185 6 : if (!(str = JS_NewUCStringCopyN(cx, p, 1)))
186 0 : return false;
187 6 : *d = STRING_TO_JSVAL(str);
188 6 : break;
189 : }
190 :
191 : case nsXPTType::T_JSVAL :
192 : {
193 218919 : *d = *((jsval*)s);
194 218919 : if (!JS_WrapValue(cx, d))
195 0 : return false;
196 218919 : break;
197 : }
198 :
199 : default:
200 :
201 : // set the default result
202 4357681 : *d = JSVAL_NULL;
203 :
204 4357681 : switch (type.TagPart()) {
205 : case nsXPTType::T_VOID:
206 0 : XPC_LOG_ERROR(("XPCConvert::NativeData2JS : void* params not supported"));
207 0 : return false;
208 :
209 : case nsXPTType::T_IID:
210 : {
211 23528 : nsID* iid2 = *((nsID**)s);
212 23528 : if (!iid2)
213 0 : break;
214 : JSObject* obj;
215 23528 : if (!(obj = xpc_NewIDObject(cx, lccx.GetScopeForNewJSObjects(), *iid2)))
216 0 : return false;
217 23528 : *d = OBJECT_TO_JSVAL(obj);
218 23528 : break;
219 : }
220 :
221 : case nsXPTType::T_ASTRING:
222 : // Fall through to T_DOMSTRING case
223 :
224 : case nsXPTType::T_DOMSTRING:
225 : {
226 1451653 : const nsAString* p = *((const nsAString**)s);
227 1451653 : if (!p)
228 0 : break;
229 :
230 1451653 : if (!p->IsVoid()) {
231 : nsStringBuffer* buf;
232 1357529 : jsval str = XPCStringConvert::ReadableToJSVal(cx, *p, &buf);
233 1357529 : if (JSVAL_IS_NULL(str))
234 0 : return false;
235 1357529 : if (buf)
236 89071 : buf->AddRef();
237 :
238 1357529 : *d = str;
239 : }
240 :
241 : // *d is defaulted to JSVAL_NULL so no need to set it
242 : // again if p is a "void" string
243 :
244 1451653 : break;
245 : }
246 :
247 : case nsXPTType::T_CHAR_STR:
248 : {
249 429574 : char* p = *((char**)s);
250 429574 : if (!p)
251 29961 : break;
252 :
253 : #ifdef STRICT_CHECK_OF_UNICODE
254 : bool isAscii = true;
255 : char* t;
256 : for (t=p; *t && isAscii ; t++) {
257 : if (ILLEGAL_CHAR_RANGE(*t))
258 : isAscii = false;
259 : }
260 : NS_ASSERTION(isAscii, "passing non ASCII data");
261 : #endif // STRICT_CHECK_OF_UNICODE
262 : JSString* str;
263 399613 : if (!(str = JS_NewStringCopyZ(cx, p)))
264 0 : return false;
265 399613 : *d = STRING_TO_JSVAL(str);
266 399613 : break;
267 : }
268 :
269 : case nsXPTType::T_WCHAR_STR:
270 : {
271 41599 : jschar* p = *((jschar**)s);
272 41599 : if (!p)
273 10436 : break;
274 : JSString* str;
275 31163 : if (!(str = JS_NewUCStringCopyZ(cx, p)))
276 0 : return false;
277 31163 : *d = STRING_TO_JSVAL(str);
278 31163 : break;
279 : }
280 : case nsXPTType::T_UTF8STRING:
281 : {
282 103997 : const nsACString* cString = *((const nsACString**)s);
283 :
284 103997 : if (!cString)
285 0 : break;
286 :
287 103997 : if (!cString->IsVoid()) {
288 : PRUint32 len;
289 102675 : jschar *p = (jschar *)UTF8ToNewUnicode(*cString, &len);
290 :
291 102675 : if (!p)
292 0 : return false;
293 :
294 : JSString* jsString =
295 : JS_NewExternalString(cx, p, len,
296 102675 : &sXPCOMUCStringFinalizer);
297 :
298 102675 : if (!jsString) {
299 0 : nsMemory::Free(p);
300 0 : return false;
301 : }
302 :
303 102675 : *d = STRING_TO_JSVAL(jsString);
304 : }
305 :
306 103997 : break;
307 :
308 : }
309 : case nsXPTType::T_CSTRING:
310 : {
311 768687 : const nsACString* cString = *((const nsACString**)s);
312 :
313 768687 : if (!cString)
314 0 : break;
315 :
316 768687 : if (!cString->IsVoid()) {
317 768686 : PRUnichar* unicodeString = ToNewUnicode(*cString);
318 768686 : if (!unicodeString)
319 0 : return false;
320 :
321 : JSString* jsString = JS_NewExternalString(cx,
322 : (jschar*)unicodeString,
323 : cString->Length(),
324 768686 : &sXPCOMUCStringFinalizer);
325 :
326 768686 : if (!jsString) {
327 0 : nsMemory::Free(unicodeString);
328 0 : return false;
329 : }
330 :
331 768686 : *d = STRING_TO_JSVAL(jsString);
332 : }
333 :
334 768687 : break;
335 : }
336 :
337 : case nsXPTType::T_INTERFACE:
338 : case nsXPTType::T_INTERFACE_IS:
339 : {
340 1538643 : nsISupports* iface = *((nsISupports**)s);
341 1538643 : if (iface) {
342 1466255 : if (iid->Equals(NS_GET_IID(nsIVariant))) {
343 253316 : nsCOMPtr<nsIVariant> variant = do_QueryInterface(iface);
344 126658 : if (!variant)
345 0 : return false;
346 :
347 : return XPCVariant::VariantDataToJS(lccx, variant,
348 126658 : pErr, d);
349 : }
350 : // else...
351 2679194 : xpcObjectHelper helper(iface);
352 1339597 : if (!NativeInterface2JSObject(lccx, d, nsnull, helper, iid,
353 1339597 : nsnull, true, pErr))
354 0 : return false;
355 :
356 : #ifdef DEBUG
357 1339597 : JSObject* jsobj = JSVAL_TO_OBJECT(*d);
358 1339597 : if (jsobj && !js::GetObjectParent(jsobj))
359 30 : NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
360 : "Why did we recreate this wrapper?");
361 : #endif
362 : }
363 1411985 : break;
364 : }
365 :
366 : default:
367 0 : NS_ERROR("bad type");
368 0 : return false;
369 : }
370 : }
371 20261805 : return true;
372 : }
373 :
374 : /***************************************************************************/
375 :
376 : #ifdef DEBUG
377 : static bool
378 220630210 : CheckJSCharInCharRange(jschar c)
379 : {
380 220630210 : if (ILLEGAL_RANGE(c)) {
381 : /* U+0080/U+0100 - U+FFFF data lost. */
382 : static const size_t MSG_BUF_SIZE = 64;
383 : char msg[MSG_BUF_SIZE];
384 0 : JS_snprintf(msg, MSG_BUF_SIZE, "jschar out of char range; high bits of data lost: 0x%x", c);
385 0 : NS_WARNING(msg);
386 0 : return false;
387 : }
388 :
389 220630210 : return true;
390 : }
391 : #endif
392 :
393 : // static
394 : JSBool
395 10136273 : XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s,
396 : const nsXPTType& type,
397 : JSBool useAllocator, const nsID* iid,
398 : nsresult* pErr)
399 : {
400 10136273 : NS_PRECONDITION(d, "bad param");
401 :
402 10136273 : JSContext* cx = ccx.GetJSContext();
403 :
404 : int32_t ti;
405 : uint32_t tu;
406 : double td;
407 : JSBool tb;
408 10136273 : JSBool isDOMString = true;
409 :
410 10136273 : if (pErr)
411 9083927 : *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
412 :
413 10136273 : switch (type.TagPart()) {
414 : case nsXPTType::T_I8 :
415 0 : if (!JS_ValueToECMAInt32(cx, s, &ti))
416 0 : return false;
417 0 : *((int8_t*)d) = int8_t(ti);
418 0 : break;
419 : case nsXPTType::T_I16 :
420 2534 : if (!JS_ValueToECMAInt32(cx, s, &ti))
421 0 : return false;
422 2534 : *((int16_t*)d) = int16_t(ti);
423 2534 : break;
424 : case nsXPTType::T_I32 :
425 44828 : if (!JS_ValueToECMAInt32(cx, s, (int32_t*)d))
426 0 : return false;
427 44828 : break;
428 : case nsXPTType::T_I64 :
429 13866 : if (JSVAL_IS_INT(s)) {
430 4278 : if (!JS_ValueToECMAInt32(cx, s, &ti))
431 0 : return false;
432 4278 : *((int64_t*)d) = ti;
433 :
434 : } else {
435 9588 : if (!JS_ValueToNumber(cx, s, &td))
436 0 : return false;
437 9588 : *((int64_t*)d) = int64_t(td);
438 : }
439 13866 : break;
440 : case nsXPTType::T_U8 :
441 4439971 : if (!JS_ValueToECMAUint32(cx, s, &tu))
442 0 : return false;
443 4439971 : *((uint8_t*)d) = uint8_t(tu);
444 4439971 : break;
445 : case nsXPTType::T_U16 :
446 3544 : if (!JS_ValueToECMAUint32(cx, s, &tu))
447 0 : return false;
448 3544 : *((uint16_t*)d) = uint16_t(tu);
449 3544 : break;
450 : case nsXPTType::T_U32 :
451 695156 : if (!JS_ValueToECMAUint32(cx, s, (uint32_t*)d))
452 0 : return false;
453 695156 : break;
454 : case nsXPTType::T_U64 :
455 13468 : if (JSVAL_IS_INT(s)) {
456 554 : if (!JS_ValueToECMAUint32(cx, s, &tu))
457 0 : return false;
458 554 : *((uint64_t*)d) = tu;
459 : } else {
460 12914 : if (!JS_ValueToNumber(cx, s, &td))
461 0 : return false;
462 12914 : *((uint64_t*)d) = uint64_t(td);
463 : }
464 13468 : break;
465 : case nsXPTType::T_FLOAT :
466 20 : if (!JS_ValueToNumber(cx, s, &td))
467 0 : return false;
468 20 : *((float*)d) = (float) td;
469 20 : break;
470 : case nsXPTType::T_DOUBLE :
471 29 : if (!JS_ValueToNumber(cx, s, (double*)d))
472 0 : return false;
473 29 : break;
474 : case nsXPTType::T_BOOL :
475 570536 : JS_ValueToBoolean(cx, s, &tb);
476 570536 : *((bool*)d) = tb;
477 570536 : break;
478 : case nsXPTType::T_CHAR :
479 : {
480 11 : JSString* str = JS_ValueToString(cx, s);
481 11 : if (!str) {
482 0 : return false;
483 : }
484 : size_t length;
485 11 : const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
486 11 : if (!chars) {
487 0 : return false;
488 : }
489 11 : jschar ch = length ? chars[0] : 0;
490 : #ifdef DEBUG
491 11 : CheckJSCharInCharRange(ch);
492 : #endif
493 11 : *((char*)d) = char(ch);
494 11 : break;
495 : }
496 : case nsXPTType::T_WCHAR :
497 : {
498 : JSString* str;
499 106 : if (!(str = JS_ValueToString(cx, s))) {
500 0 : return false;
501 : }
502 : size_t length;
503 106 : const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
504 106 : if (!chars) {
505 0 : return false;
506 : }
507 106 : if (length == 0) {
508 0 : *((uint16_t*)d) = 0;
509 0 : break;
510 : }
511 106 : *((uint16_t*)d) = uint16_t(chars[0]);
512 106 : break;
513 : }
514 : case nsXPTType::T_JSVAL :
515 222255 : *((jsval*)d) = s;
516 222255 : break;
517 : default:
518 :
519 4129949 : switch (type.TagPart()) {
520 : case nsXPTType::T_VOID:
521 0 : XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported"));
522 0 : NS_ERROR("void* params not supported");
523 0 : return false;
524 : case nsXPTType::T_IID:
525 : {
526 : JSObject* obj;
527 40733 : const nsID* pid=nsnull;
528 :
529 : // There's no good reason to pass a null IID.
530 40733 : if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
531 0 : if (pErr)
532 0 : *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
533 0 : return false;
534 : }
535 :
536 81466 : if (!JSVAL_IS_OBJECT(s) ||
537 : (!(obj = JSVAL_TO_OBJECT(s))) ||
538 : (!(pid = xpc_JSObjectToID(cx, obj))) ||
539 40733 : (!(pid = (const nsID*) nsMemory::Clone(pid, sizeof(nsID))))) {
540 0 : return false;
541 : }
542 40733 : *((const nsID**)d) = pid;
543 40733 : return true;
544 : }
545 :
546 : case nsXPTType::T_ASTRING:
547 : {
548 911812 : isDOMString = false;
549 : // Fall through to T_DOMSTRING case.
550 : }
551 : case nsXPTType::T_DOMSTRING:
552 : {
553 : static const PRUnichar EMPTY_STRING[] = { '\0' };
554 : static const PRUnichar VOID_STRING[] = { 'u', 'n', 'd', 'e', 'f', 'i', 'n', 'e', 'd', '\0' };
555 :
556 911971 : const PRUnichar* chars = nsnull;
557 911971 : JSString* str = nsnull;
558 911971 : JSBool isNewString = false;
559 911971 : PRUint32 length = 0;
560 :
561 911971 : if (JSVAL_IS_VOID(s)) {
562 582 : if (isDOMString) {
563 0 : chars = VOID_STRING;
564 0 : length = ArrayLength(VOID_STRING) - 1;
565 : } else {
566 582 : chars = EMPTY_STRING;
567 582 : length = 0;
568 : }
569 911389 : } else if (!JSVAL_IS_NULL(s)) {
570 818189 : str = JS_ValueToString(cx, s);
571 818189 : if (!str)
572 0 : return false;
573 :
574 818189 : length = (PRUint32) JS_GetStringLength(str);
575 818189 : if (length) {
576 812614 : chars = JS_GetStringCharsZ(cx, str);
577 812614 : if (!chars)
578 0 : return false;
579 812614 : if (STRING_TO_JSVAL(str) != s)
580 17 : isNewString = true;
581 : } else {
582 5575 : str = nsnull;
583 5575 : chars = EMPTY_STRING;
584 : }
585 : }
586 :
587 911971 : if (useAllocator) {
588 : // XXX extra string copy when isNewString
589 284915 : if (str && !isNewString) {
590 : size_t strLength;
591 258385 : const jschar *strChars = JS_GetStringCharsZAndLength(cx, str, &strLength);
592 258385 : if (!strChars)
593 0 : return false;
594 :
595 : XPCReadableJSStringWrapper *wrapper =
596 258385 : ccx.NewStringWrapper(strChars, strLength);
597 258385 : if (!wrapper)
598 0 : return false;
599 :
600 258385 : *((const nsAString**)d) = wrapper;
601 26530 : } else if (JSVAL_IS_NULL(s)) {
602 : XPCReadableJSStringWrapper *wrapper =
603 22486 : new XPCReadableJSStringWrapper();
604 22486 : if (!wrapper)
605 0 : return false;
606 :
607 22486 : *((const nsAString**)d) = wrapper;
608 : } else {
609 : // use nsString to encourage sharing
610 4044 : const nsAString *rs = new nsString(chars, length);
611 4044 : if (!rs)
612 0 : return false;
613 4044 : *((const nsAString**)d) = rs;
614 : }
615 : } else {
616 627056 : nsAString* ws = *((nsAString**)d);
617 :
618 627056 : if (JSVAL_IS_NULL(s) || (!isDOMString && JSVAL_IS_VOID(s))) {
619 70733 : ws->Truncate();
620 70733 : ws->SetIsVoid(true);
621 : } else
622 556323 : ws->Assign(chars, length);
623 : }
624 911971 : return true;
625 : }
626 :
627 : case nsXPTType::T_CHAR_STR:
628 : {
629 766890 : if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
630 35429 : *((char**)d) = nsnull;
631 35429 : return true;
632 : }
633 :
634 731461 : JSString* str = JS_ValueToString(cx, s);
635 731461 : if (!str) {
636 0 : return false;
637 : }
638 : #ifdef DEBUG
639 731461 : const jschar* chars=nsnull;
640 731461 : if (nsnull != (chars = JS_GetStringCharsZ(cx, str))) {
641 731461 : bool legalRange = true;
642 731461 : int len = JS_GetStringLength(str);
643 : const jschar* t;
644 731461 : PRInt32 i=0;
645 221361660 : for (t=chars; (i< len) && legalRange ; i++,t++) {
646 220630199 : if (!CheckJSCharInCharRange(*t))
647 0 : break;
648 : }
649 : }
650 : #endif // DEBUG
651 731461 : size_t length = JS_GetStringEncodingLength(cx, str);
652 731461 : if (length == size_t(-1)) {
653 0 : return false;
654 : }
655 731461 : char *buffer = static_cast<char *>(nsMemory::Alloc(length + 1));
656 731461 : if (!buffer) {
657 0 : return false;
658 : }
659 731461 : JS_EncodeStringToBuffer(str, buffer, length);
660 731461 : buffer[length] = '\0';
661 731461 : *((void**)d) = buffer;
662 731461 : return true;
663 : }
664 :
665 : case nsXPTType::T_WCHAR_STR:
666 : {
667 45441 : const jschar* chars=nsnull;
668 : JSString* str;
669 :
670 45441 : if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
671 13942 : *((jschar**)d) = nsnull;
672 13942 : return true;
673 : }
674 :
675 31499 : if (!(str = JS_ValueToString(cx, s))) {
676 0 : return false;
677 : }
678 31499 : if (!(chars = JS_GetStringCharsZ(cx, str))) {
679 0 : return false;
680 : }
681 31499 : int len = JS_GetStringLength(str);
682 31499 : int byte_len = (len+1)*sizeof(jschar);
683 31499 : if (!(*((void**)d) = nsMemory::Alloc(byte_len))) {
684 : // XXX should report error
685 0 : return false;
686 : }
687 31499 : jschar* destchars = *((jschar**)d);
688 31499 : memcpy(destchars, chars, byte_len);
689 31499 : destchars[len] = 0;
690 :
691 31499 : return true;
692 : }
693 :
694 : case nsXPTType::T_UTF8STRING:
695 : {
696 : const jschar* chars;
697 : PRUint32 length;
698 : JSString* str;
699 :
700 347770 : if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) {
701 2778 : if (useAllocator) {
702 2458 : nsACString *rs = new nsCString();
703 2458 : if (!rs)
704 0 : return false;
705 :
706 2458 : rs->SetIsVoid(true);
707 2458 : *((nsACString**)d) = rs;
708 : } else {
709 320 : nsCString* rs = *((nsCString**)d);
710 320 : rs->Truncate();
711 320 : rs->SetIsVoid(true);
712 : }
713 2778 : return true;
714 : }
715 :
716 : // The JS val is neither null nor void...
717 :
718 344992 : if (!(str = JS_ValueToString(cx, s))||
719 : !(chars = JS_GetStringCharsZ(cx, str))) {
720 0 : return false;
721 : }
722 :
723 344992 : length = JS_GetStringLength(str);
724 :
725 : nsCString *rs;
726 344992 : if (useAllocator) {
727 : // Use nsCString to enable sharing
728 313434 : rs = new nsCString();
729 313434 : if (!rs)
730 0 : return false;
731 :
732 313434 : *((const nsCString**)d) = rs;
733 : } else {
734 31558 : rs = *((nsCString**)d);
735 : }
736 344992 : const PRUnichar* start = (const PRUnichar*)chars;
737 344992 : const PRUnichar* end = start + length;
738 344992 : CopyUTF16toUTF8(nsDependentSubstring(start, end), *rs);
739 344992 : return true;
740 : }
741 :
742 : case nsXPTType::T_CSTRING:
743 : {
744 758192 : if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) {
745 86 : if (useAllocator) {
746 86 : nsACString *rs = new nsCString();
747 86 : if (!rs)
748 0 : return false;
749 :
750 86 : rs->SetIsVoid(true);
751 86 : *((nsACString**)d) = rs;
752 : } else {
753 0 : nsACString* rs = *((nsACString**)d);
754 0 : rs->Truncate();
755 0 : rs->SetIsVoid(true);
756 : }
757 86 : return true;
758 : }
759 :
760 : // The JS val is neither null nor void...
761 758106 : JSString* str = JS_ValueToString(cx, s);
762 758106 : if (!str) {
763 0 : return false;
764 : }
765 :
766 758106 : size_t length = JS_GetStringEncodingLength(cx, str);
767 758106 : if (length == size_t(-1)) {
768 0 : return false;
769 : }
770 :
771 : nsACString *rs;
772 758106 : if (useAllocator) {
773 675871 : rs = new nsCString();
774 675871 : if (!rs)
775 0 : return false;
776 675871 : *((const nsACString**)d) = rs;
777 : } else {
778 82235 : rs = *((nsACString**)d);
779 : }
780 :
781 758106 : rs->SetLength(PRUint32(length));
782 758106 : if (rs->Length() != PRUint32(length)) {
783 0 : return false;
784 : }
785 758106 : JS_EncodeStringToBuffer(str, rs->BeginWriting(), length);
786 :
787 758106 : return true;
788 : }
789 :
790 : case nsXPTType::T_INTERFACE:
791 : case nsXPTType::T_INTERFACE_IS:
792 : {
793 : JSObject* obj;
794 1258952 : NS_ASSERTION(iid,"can't do interface conversions without iid");
795 :
796 1258952 : if (iid->Equals(NS_GET_IID(nsIVariant))) {
797 5919 : XPCVariant* variant = XPCVariant::newVariant(ccx, s);
798 5919 : if (!variant)
799 0 : return false;
800 5919 : *((nsISupports**)d) = static_cast<nsIVariant*>(variant);
801 5919 : return true;
802 1253033 : } else if (iid->Equals(NS_GET_IID(nsIAtom)) &&
803 0 : JSVAL_IS_STRING(s)) {
804 : // We're trying to pass a string as an nsIAtom. Let's atomize!
805 0 : JSString* str = JSVAL_TO_STRING(s);
806 0 : const PRUnichar* chars = JS_GetStringCharsZ(cx, str);
807 0 : if (!chars) {
808 0 : if (pErr)
809 0 : *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF;
810 0 : return false;
811 : }
812 0 : PRUint32 length = JS_GetStringLength(str);
813 : nsIAtom* atom = NS_NewAtom(nsDependentSubstring(chars,
814 0 : chars + length));
815 0 : if (!atom && pErr)
816 0 : *pErr = NS_ERROR_OUT_OF_MEMORY;
817 0 : *((nsISupports**)d) = atom;
818 0 : return atom != nsnull;
819 : }
820 : //else ...
821 :
822 1253033 : if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
823 104904 : *((nsISupports**)d) = nsnull;
824 104904 : return true;
825 : }
826 :
827 : // only wrap JSObjects
828 1148129 : if (!JSVAL_IS_OBJECT(s) || !(obj = JSVAL_TO_OBJECT(s))) {
829 1 : if (pErr && JSVAL_IS_INT(s) && 0 == JSVAL_TO_INT(s))
830 0 : *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL;
831 1 : return false;
832 : }
833 :
834 : return JSObject2NativeInterface(ccx, (void**)d, obj, iid,
835 1148128 : nsnull, pErr);
836 : }
837 : default:
838 0 : NS_ERROR("bad type");
839 0 : return false;
840 : }
841 : }
842 6006324 : return true;
843 : }
844 :
845 : inline JSBool
846 4416 : CreateHolderIfNeeded(XPCCallContext& ccx, JSObject* obj, jsval* d,
847 : nsIXPConnectJSObjectHolder** dest)
848 : {
849 4416 : if (dest) {
850 0 : XPCJSObjectHolder* objHolder = XPCJSObjectHolder::newHolder(ccx, obj);
851 0 : if (!objHolder)
852 0 : return false;
853 :
854 0 : NS_ADDREF(*dest = objHolder);
855 : }
856 :
857 4416 : *d = OBJECT_TO_JSVAL(obj);
858 :
859 4416 : return true;
860 : }
861 :
862 : /***************************************************************************/
863 : // static
864 : JSBool
865 2027154 : XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx,
866 : jsval* d,
867 : nsIXPConnectJSObjectHolder** dest,
868 : xpcObjectHelper& aHelper,
869 : const nsID* iid,
870 : XPCNativeInterface** Interface,
871 : bool allowNativeWrapper,
872 : nsresult* pErr)
873 : {
874 2027154 : NS_ASSERTION(!Interface || iid,
875 : "Need the iid if you pass in an XPCNativeInterface cache.");
876 :
877 2027154 : *d = JSVAL_NULL;
878 2027154 : if (dest)
879 545917 : *dest = nsnull;
880 2027154 : nsISupports *src = aHelper.Object();
881 2027154 : if (!src)
882 0 : return true;
883 2027154 : if (pErr)
884 1689764 : *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
885 :
886 : // We used to have code here that unwrapped and simply exposed the
887 : // underlying JSObject. That caused anomolies when JSComponents were
888 : // accessed from other JS code - they didn't act like other xpconnect
889 : // wrapped components. So, instead, we create "double wrapped" objects
890 : // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't
891 : // optimal -- we could detect this and roll the functionality into a
892 : // single wrapper, but the current solution is good enough for now.
893 2027154 : JSContext* cx = lccx.GetJSContext();
894 2027154 : NS_ABORT_IF_FALSE(js::IsObjectInContextCompartment(lccx.GetScopeForNewJSObjects(), cx),
895 : "bad scope for new JSObjects");
896 :
897 2027154 : JSObject *jsscope = lccx.GetScopeForNewJSObjects();
898 : XPCWrappedNativeScope* xpcscope =
899 2027154 : XPCWrappedNativeScope::FindInJSObjectScope(cx, jsscope);
900 2027154 : if (!xpcscope)
901 0 : return false;
902 :
903 : // First, see if this object supports the wrapper cache.
904 : // Note: If |cache->IsProxy()| is true, then it means that the object
905 : // implementing it doesn't want a wrapped native as its JS Object, but
906 : // instead it provides its own proxy object. In that case, the object
907 : // to use is found as cache->GetWrapper(). If that is null, then the
908 : // object will create (and fill the cache) from its WrapObject call.
909 2027154 : nsWrapperCache *cache = aHelper.GetWrapperCache();
910 :
911 2027154 : bool tryConstructSlimWrapper = false;
912 : JSObject *flat;
913 2027154 : if (cache) {
914 40218 : flat = cache->GetWrapper();
915 40218 : if (cache->IsProxy()) {
916 4416 : XPCCallContext &ccx = lccx.GetXPCCallContext();
917 4416 : if (!ccx.IsValid())
918 0 : return false;
919 :
920 4416 : if (!flat) {
921 : bool triedToWrap;
922 : flat = cache->WrapObject(lccx.GetJSContext(), xpcscope,
923 4321 : &triedToWrap);
924 4321 : if (!flat && triedToWrap)
925 0 : return false;
926 : }
927 :
928 4416 : if (flat) {
929 4416 : if (!JS_WrapObject(ccx, &flat))
930 0 : return false;
931 :
932 4416 : return CreateHolderIfNeeded(ccx, flat, d, dest);
933 : }
934 : }
935 :
936 35802 : if (!dest) {
937 35802 : if (!flat) {
938 19761 : tryConstructSlimWrapper = true;
939 16041 : } else if (IS_SLIM_WRAPPER_OBJECT(flat)) {
940 6401 : if (js::IsObjectInContextCompartment(flat, cx)) {
941 6401 : *d = OBJECT_TO_JSVAL(flat);
942 6401 : return true;
943 : }
944 : }
945 : }
946 : } else {
947 1986936 : flat = nsnull;
948 : }
949 :
950 : // If we're not handing this wrapper to an nsIXPConnectJSObjectHolder, and
951 : // the object supports slim wrappers, try to create one here.
952 2016337 : if (tryConstructSlimWrapper) {
953 19761 : XPCCallContext &ccx = lccx.GetXPCCallContext();
954 19761 : if (!ccx.IsValid())
955 0 : return false;
956 :
957 : jsval slim;
958 19761 : if (ConstructSlimWrapper(ccx, aHelper, xpcscope, &slim)) {
959 15155 : *d = slim;
960 15155 : return true;
961 : }
962 :
963 4606 : if (JS_IsExceptionPending(cx))
964 0 : return false;
965 :
966 : // Even if ConstructSlimWrapper returns false it might have created a
967 : // wrapper (while calling the PreCreate hook). In that case we need to
968 : // fall through because we either have a slim wrapper that needs to be
969 : // morphed or an XPCWrappedNative.
970 4606 : flat = cache->GetWrapper();
971 : }
972 :
973 : // We can't simply construct a slim wrapper. Go ahead and create an
974 : // XPCWrappedNative for this object. At this point, |flat| could be
975 : // non-null, meaning that either we already have a wrapped native from
976 : // the cache (which might need to be QI'd to the new interface) or that
977 : // we found a slim wrapper that we'll have to morph.
978 4002364 : AutoMarkingNativeInterfacePtr iface;
979 2001182 : if (iid) {
980 1995117 : XPCCallContext &ccx = lccx.GetXPCCallContext();
981 1995117 : if (!ccx.IsValid())
982 0 : return false;
983 :
984 1995117 : iface.Init(ccx);
985 :
986 1995117 : if (Interface)
987 10939 : iface = *Interface;
988 :
989 1995117 : if (!iface) {
990 1984593 : iface = XPCNativeInterface::GetNewOrUsed(ccx, iid);
991 1984593 : if (!iface)
992 0 : return false;
993 :
994 1984593 : if (Interface)
995 415 : *Interface = iface;
996 : }
997 : }
998 :
999 2001182 : NS_ASSERTION(!flat || IS_WRAPPER_CLASS(js::GetObjectClass(flat)),
1000 : "What kind of wrapper is this?");
1001 :
1002 : nsresult rv;
1003 : XPCWrappedNative* wrapper;
1004 4002364 : nsRefPtr<XPCWrappedNative> strongWrapper;
1005 2001182 : if (!flat) {
1006 1991542 : XPCCallContext &ccx = lccx.GetXPCCallContext();
1007 1991542 : if (!ccx.IsValid())
1008 0 : return false;
1009 :
1010 : rv = XPCWrappedNative::GetNewOrUsed(ccx, aHelper, xpcscope, iface,
1011 1991542 : getter_AddRefs(strongWrapper));
1012 :
1013 1991542 : wrapper = strongWrapper;
1014 9640 : } else if (IS_WN_WRAPPER_OBJECT(flat)) {
1015 9640 : wrapper = static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(flat));
1016 :
1017 : // If asked to return the wrapper we'll return a strong reference,
1018 : // otherwise we'll just return its JSObject in d (which should be
1019 : // rooted in that case).
1020 9640 : if (dest)
1021 0 : strongWrapper = wrapper;
1022 : // If iface is not null we know lccx.GetXPCCallContext() returns
1023 : // a valid XPCCallContext because we checked when calling Init on
1024 : // iface.
1025 9640 : if (iface)
1026 5530 : wrapper->FindTearOff(lccx.GetXPCCallContext(), iface, false,
1027 11060 : &rv);
1028 : else
1029 4110 : rv = NS_OK;
1030 : } else {
1031 0 : NS_ASSERTION(IS_SLIM_WRAPPER(flat),
1032 : "What kind of wrapper is this?");
1033 :
1034 0 : XPCCallContext &ccx = lccx.GetXPCCallContext();
1035 0 : if (!ccx.IsValid())
1036 0 : return false;
1037 :
1038 : SLIM_LOG(("***** morphing from XPCConvert::NativeInterface2JSObject"
1039 : "(%p)\n",
1040 : static_cast<nsISupports*>(xpc_GetJSPrivate(flat))));
1041 :
1042 : rv = XPCWrappedNative::Morph(ccx, flat, iface, cache,
1043 0 : getter_AddRefs(strongWrapper));
1044 0 : wrapper = strongWrapper;
1045 : }
1046 :
1047 2001182 : if (NS_FAILED(rv) && pErr)
1048 0 : *pErr = rv;
1049 :
1050 : // If creating the wrapped native failed, then return early.
1051 2001182 : if (NS_FAILED(rv) || !wrapper)
1052 0 : return false;
1053 :
1054 : // If we're not creating security wrappers, we can return the
1055 : // XPCWrappedNative as-is here.
1056 2001182 : flat = wrapper->GetFlatJSObject();
1057 2001182 : jsval v = OBJECT_TO_JSVAL(flat);
1058 4002364 : if (!XPCPerThreadData::IsMainThread(lccx.GetJSContext()) ||
1059 2001182 : !allowNativeWrapper) {
1060 551982 : *d = v;
1061 551982 : if (dest)
1062 545917 : *dest = strongWrapper.forget().get();
1063 551982 : if (pErr)
1064 548017 : *pErr = NS_OK;
1065 551982 : return true;
1066 : }
1067 :
1068 1449200 : XPCCallContext &ccx = lccx.GetXPCCallContext();
1069 1449200 : if (!ccx.IsValid())
1070 0 : return false;
1071 :
1072 1449200 : JSObject *original = flat;
1073 1449200 : if (!JS_WrapObject(ccx, &flat))
1074 0 : return false;
1075 :
1076 : // If the object was not wrapped, we are same compartment and don't need
1077 : // to enforce any cross origin policies, except in case of the location
1078 : // object, which always needs a wrapper in between.
1079 1449200 : if (original == flat) {
1080 1449200 : if (xpc::WrapperFactory::IsLocationObject(flat)) {
1081 0 : JSObject *locationWrapper = wrapper->GetWrapper();
1082 0 : if (!locationWrapper) {
1083 0 : locationWrapper = xpc::WrapperFactory::WrapLocationObject(cx, flat);
1084 0 : if (!locationWrapper)
1085 0 : return false;
1086 :
1087 : // Cache the location wrapper to ensure that we maintain
1088 : // the identity of window/document.location.
1089 0 : wrapper->SetWrapper(locationWrapper);
1090 : }
1091 :
1092 0 : flat = locationWrapper;
1093 1449200 : } else if (wrapper->NeedsSOW() &&
1094 0 : !xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
1095 0 : JSObject *sowWrapper = wrapper->GetWrapper();
1096 0 : if (!sowWrapper) {
1097 0 : sowWrapper = xpc::WrapperFactory::WrapSOWObject(cx, flat);
1098 0 : if (!sowWrapper)
1099 0 : return false;
1100 :
1101 : // Cache the sow wrapper to ensure that we maintain
1102 : // the identity of this node.
1103 0 : wrapper->SetWrapper(sowWrapper);
1104 : }
1105 :
1106 0 : flat = sowWrapper;
1107 : } else {
1108 1449200 : flat = JS_ObjectToOuterObject(cx, flat);
1109 1449200 : NS_ASSERTION(flat, "bad outer object hook!");
1110 1449200 : NS_ASSERTION(js::IsObjectInContextCompartment(flat, cx),
1111 : "bad compartment");
1112 : }
1113 : }
1114 :
1115 1449200 : *d = OBJECT_TO_JSVAL(flat);
1116 :
1117 1449200 : if (dest) {
1118 : // The strongWrapper still holds the original flat object.
1119 0 : if (flat == original) {
1120 0 : *dest = strongWrapper.forget().get();
1121 : } else {
1122 : nsRefPtr<XPCJSObjectHolder> objHolder =
1123 0 : XPCJSObjectHolder::newHolder(ccx, flat);
1124 0 : if (!objHolder)
1125 0 : return false;
1126 :
1127 0 : *dest = objHolder.forget().get();
1128 : }
1129 : }
1130 :
1131 1449200 : if (pErr)
1132 1116576 : *pErr = NS_OK;
1133 :
1134 1449200 : return true;
1135 : }
1136 :
1137 : /***************************************************************************/
1138 :
1139 : // static
1140 : JSBool
1141 1150580 : XPCConvert::JSObject2NativeInterface(XPCCallContext& ccx,
1142 : void** dest, JSObject* src,
1143 : const nsID* iid,
1144 : nsISupports* aOuter,
1145 : nsresult* pErr)
1146 : {
1147 1150580 : NS_ASSERTION(dest, "bad param");
1148 1150580 : NS_ASSERTION(src, "bad param");
1149 1150580 : NS_ASSERTION(iid, "bad param");
1150 :
1151 1150580 : JSContext* cx = ccx.GetJSContext();
1152 :
1153 2301160 : JSAutoEnterCompartment ac;
1154 :
1155 1150580 : if (!ac.enter(cx, src)) {
1156 0 : if (pErr)
1157 0 : *pErr = NS_ERROR_UNEXPECTED;
1158 0 : return false;
1159 : }
1160 :
1161 1150580 : *dest = nsnull;
1162 1150580 : if (pErr)
1163 1130101 : *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1164 :
1165 : nsISupports* iface;
1166 :
1167 1150580 : if (!aOuter) {
1168 : // Note that if we have a non-null aOuter then it means that we are
1169 : // forcing the creation of a wrapper even if the object *is* a
1170 : // wrappedNative or other wise has 'nsISupportness'.
1171 : // This allows wrapJSAggregatedToNative to work.
1172 :
1173 : // If we're looking at a security wrapper, see now if we're allowed to
1174 : // pass it to C++. If we are, then fall through to the code below. If
1175 : // we aren't, throw an exception eagerly.
1176 1150580 : JSObject* inner = nsnull;
1177 1150580 : if (XPCWrapper::IsSecurityWrapper(src)) {
1178 46 : inner = XPCWrapper::Unwrap(cx, src, false);
1179 46 : if (!inner) {
1180 0 : if (pErr)
1181 0 : *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO;
1182 0 : return false;
1183 : }
1184 : }
1185 :
1186 : // Is this really a native xpcom object with a wrapper?
1187 : XPCWrappedNative* wrappedNative =
1188 : XPCWrappedNative::GetWrappedNativeOfJSObject(cx,
1189 : inner
1190 : ? inner
1191 1150580 : : src);
1192 1150580 : if (wrappedNative) {
1193 1007859 : iface = wrappedNative->GetIdentityObject();
1194 1007859 : return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
1195 : }
1196 : // else...
1197 :
1198 : // XXX E4X breaks the world. Don't try wrapping E4X objects!
1199 : // This hack can be removed (or changed accordingly) when the
1200 : // DOM <-> E4X bindings are complete, see bug 270553
1201 142721 : if (JS_TypeOfValue(cx, OBJECT_TO_JSVAL(src)) == JSTYPE_XML)
1202 0 : return false;
1203 :
1204 : // Deal with slim wrappers here.
1205 142721 : if (GetISupportsFromJSObject(src, &iface)) {
1206 415 : if (iface)
1207 415 : return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
1208 :
1209 0 : return false;
1210 : }
1211 : }
1212 :
1213 : // else...
1214 :
1215 : nsXPCWrappedJS* wrapper;
1216 142306 : nsresult rv = nsXPCWrappedJS::GetNewOrUsed(ccx, src, *iid, aOuter, &wrapper);
1217 142306 : if (pErr)
1218 128819 : *pErr = rv;
1219 142306 : if (NS_SUCCEEDED(rv) && wrapper) {
1220 : // We need to go through the QueryInterface logic to make this return
1221 : // the right thing for the various 'special' interfaces; e.g.
1222 : // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
1223 : // there is an outer to avoid nasty recursion.
1224 0 : rv = aOuter ? wrapper->AggregatedQueryInterface(*iid, dest) :
1225 142306 : wrapper->QueryInterface(*iid, dest);
1226 142306 : if (pErr)
1227 128819 : *pErr = rv;
1228 142306 : NS_RELEASE(wrapper);
1229 142306 : return NS_SUCCEEDED(rv);
1230 : }
1231 :
1232 : // else...
1233 0 : return false;
1234 : }
1235 :
1236 : /***************************************************************************/
1237 : /***************************************************************************/
1238 :
1239 : // static
1240 : nsresult
1241 6716 : XPCConvert::ConstructException(nsresult rv, const char* message,
1242 : const char* ifaceName, const char* methodName,
1243 : nsISupports* data,
1244 : nsIException** exceptn,
1245 : JSContext* cx,
1246 : jsval* jsExceptionPtr)
1247 : {
1248 6716 : NS_ASSERTION(!cx == !jsExceptionPtr, "Expected cx and jsExceptionPtr to cooccur.");
1249 :
1250 : static const char format[] = "\'%s\' when calling method: [%s::%s]";
1251 6716 : const char * msg = message;
1252 6716 : char* sz = nsnull;
1253 13432 : nsXPIDLString xmsg;
1254 13432 : nsCAutoString sxmsg;
1255 :
1256 13432 : nsCOMPtr<nsIScriptError> errorObject = do_QueryInterface(data);
1257 6716 : if (errorObject) {
1258 256 : if (NS_SUCCEEDED(errorObject->GetMessageMoz(getter_Copies(xmsg)))) {
1259 256 : CopyUTF16toUTF8(xmsg, sxmsg);
1260 256 : msg = sxmsg.get();
1261 : }
1262 : }
1263 6716 : if (!msg)
1264 6292 : if (!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &msg) || ! msg)
1265 0 : msg = "<error>";
1266 6716 : if (ifaceName && methodName)
1267 6716 : msg = sz = JS_smprintf(format, msg, ifaceName, methodName);
1268 :
1269 6716 : nsresult res = nsXPCException::NewException(msg, rv, nsnull, data, exceptn);
1270 :
1271 6716 : if (NS_SUCCEEDED(res) && cx && jsExceptionPtr && *exceptn) {
1272 12742 : nsCOMPtr<nsIXPCException> xpcEx = do_QueryInterface(*exceptn);
1273 6371 : if (xpcEx)
1274 6371 : xpcEx->StowJSVal(cx, *jsExceptionPtr);
1275 : }
1276 :
1277 6716 : if (sz)
1278 6716 : JS_smprintf_free(sz);
1279 6716 : return res;
1280 : }
1281 :
1282 : /********************************/
1283 :
1284 : class AutoExceptionRestorer
1285 : {
1286 : public:
1287 6801 : AutoExceptionRestorer(JSContext *cx, jsval v)
1288 6801 : : mContext(cx), tvr(cx, v)
1289 : {
1290 6801 : JS_ClearPendingException(mContext);
1291 6801 : }
1292 :
1293 6801 : ~AutoExceptionRestorer()
1294 6801 : {
1295 6801 : JS_SetPendingException(mContext, tvr.jsval_value());
1296 6801 : }
1297 :
1298 : private:
1299 : JSContext * const mContext;
1300 : JS::AutoValueRooter tvr;
1301 : };
1302 :
1303 : // static
1304 : nsresult
1305 6801 : XPCConvert::JSValToXPCException(XPCCallContext& ccx,
1306 : jsval s,
1307 : const char* ifaceName,
1308 : const char* methodName,
1309 : nsIException** exceptn)
1310 : {
1311 6801 : JSContext* cx = ccx.GetJSContext();
1312 13602 : AutoExceptionRestorer aer(cx, s);
1313 :
1314 6801 : if (!JSVAL_IS_PRIMITIVE(s)) {
1315 : // we have a JSObject
1316 433 : JSObject* obj = JSVAL_TO_OBJECT(s);
1317 :
1318 433 : if (!obj) {
1319 0 : NS_ERROR("when is an object not an object?");
1320 0 : return NS_ERROR_FAILURE;
1321 : }
1322 :
1323 : // is this really a native xpcom object with a wrapper?
1324 : XPCWrappedNative* wrapper;
1325 433 : if (nsnull != (wrapper =
1326 : XPCWrappedNative::GetWrappedNativeOfJSObject(cx,obj))) {
1327 173 : nsISupports* supports = wrapper->GetIdentityObject();
1328 346 : nsCOMPtr<nsIException> iface = do_QueryInterface(supports);
1329 173 : if (iface) {
1330 : // just pass through the exception (with extra ref and all)
1331 172 : nsIException* temp = iface;
1332 172 : NS_ADDREF(temp);
1333 172 : *exceptn = temp;
1334 172 : return NS_OK;
1335 : } else {
1336 : // it is a wrapped native, but not an exception!
1337 : return ConstructException(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT,
1338 : nsnull, ifaceName, methodName, supports,
1339 1 : exceptn, nsnull, nsnull);
1340 : }
1341 : } else {
1342 : // It is a JSObject, but not a wrapped native...
1343 :
1344 : // If it is an engine Error with an error report then let's
1345 : // extract the report and build an xpcexception from that
1346 : const JSErrorReport* report;
1347 260 : if (nsnull != (report = JS_ErrorFromException(cx, s))) {
1348 512 : JSAutoByteString message;
1349 : JSString* str;
1350 256 : if (nsnull != (str = JS_ValueToString(cx, s)))
1351 256 : message.encode(cx, str);
1352 256 : return JSErrorToXPCException(ccx, message.ptr(), ifaceName,
1353 256 : methodName, report, exceptn);
1354 : }
1355 :
1356 :
1357 : unsigned ignored;
1358 : JSBool found;
1359 :
1360 : // heuristic to see if it might be usable as an xpcexception
1361 4 : if (!JS_GetPropertyAttributes(cx, obj, "message", &ignored, &found))
1362 0 : return NS_ERROR_FAILURE;
1363 :
1364 4 : if (found && !JS_GetPropertyAttributes(cx, obj, "result", &ignored, &found))
1365 0 : return NS_ERROR_FAILURE;
1366 :
1367 4 : if (found) {
1368 : // lets try to build a wrapper around the JSObject
1369 : nsXPCWrappedJS* jswrapper;
1370 : nsresult rv =
1371 : nsXPCWrappedJS::GetNewOrUsed(ccx, obj,
1372 : NS_GET_IID(nsIException),
1373 1 : nsnull, &jswrapper);
1374 1 : if (NS_FAILED(rv))
1375 0 : return rv;
1376 :
1377 1 : *exceptn = static_cast<nsIException *>(jswrapper->GetXPTCStub());
1378 1 : return NS_OK;
1379 : }
1380 :
1381 :
1382 : // XXX we should do a check against 'js_ErrorClass' here and
1383 : // do the right thing - even though it has no JSErrorReport,
1384 : // The fact that it is a JSError exceptions means we can extract
1385 : // particular info and our 'result' should reflect that.
1386 :
1387 : // otherwise we'll just try to convert it to a string
1388 :
1389 3 : JSString* str = JS_ValueToString(cx, s);
1390 3 : if (!str)
1391 0 : return NS_ERROR_FAILURE;
1392 :
1393 6 : JSAutoByteString strBytes(cx, str);
1394 3 : if (!strBytes)
1395 0 : return NS_ERROR_FAILURE;
1396 :
1397 : return ConstructException(NS_ERROR_XPC_JS_THREW_JS_OBJECT,
1398 3 : strBytes.ptr(), ifaceName, methodName,
1399 3 : nsnull, exceptn, cx, &s);
1400 : }
1401 : }
1402 :
1403 6368 : if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
1404 : return ConstructException(NS_ERROR_XPC_JS_THREW_NULL,
1405 : nsnull, ifaceName, methodName, nsnull,
1406 0 : exceptn, cx, &s);
1407 : }
1408 :
1409 6368 : if (JSVAL_IS_NUMBER(s)) {
1410 : // lets see if it looks like an nsresult
1411 : nsresult rv;
1412 : double number;
1413 6291 : JSBool isResult = false;
1414 :
1415 6291 : if (JSVAL_IS_INT(s)) {
1416 0 : rv = (nsresult) JSVAL_TO_INT(s);
1417 0 : if (NS_FAILED(rv))
1418 0 : isResult = true;
1419 : else
1420 0 : number = (double) JSVAL_TO_INT(s);
1421 : } else {
1422 6291 : number = JSVAL_TO_DOUBLE(s);
1423 12582 : if (number > 0.0 &&
1424 : number < (double)0xffffffff &&
1425 6291 : 0.0 == fmod(number,1)) {
1426 6291 : rv = (nsresult) number;
1427 6291 : if (NS_FAILED(rv))
1428 6291 : isResult = true;
1429 : }
1430 : }
1431 :
1432 6291 : if (isResult)
1433 : return ConstructException(rv, nsnull, ifaceName, methodName,
1434 6291 : nsnull, exceptn, cx, &s);
1435 : else {
1436 : // XXX all this nsISupportsDouble code seems a little redundant
1437 : // now that we're storing the jsval in the exception...
1438 : nsISupportsDouble* data;
1439 0 : nsCOMPtr<nsIComponentManager> cm;
1440 0 : if (NS_FAILED(NS_GetComponentManager(getter_AddRefs(cm))) || !cm ||
1441 0 : NS_FAILED(cm->CreateInstanceByContractID(NS_SUPPORTS_DOUBLE_CONTRACTID,
1442 : nsnull,
1443 : NS_GET_IID(nsISupportsDouble),
1444 : (void**)&data)))
1445 0 : return NS_ERROR_FAILURE;
1446 0 : data->SetData(number);
1447 : rv = ConstructException(NS_ERROR_XPC_JS_THREW_NUMBER, nsnull,
1448 0 : ifaceName, methodName, data, exceptn, cx, &s);
1449 0 : NS_RELEASE(data);
1450 0 : return rv;
1451 : }
1452 : }
1453 :
1454 : // otherwise we'll just try to convert it to a string
1455 : // Note: e.g., JSBools get converted to JSStrings by this code.
1456 :
1457 77 : JSString* str = JS_ValueToString(cx, s);
1458 77 : if (str) {
1459 154 : JSAutoByteString strBytes(cx, str);
1460 77 : if (!!strBytes) {
1461 : return ConstructException(NS_ERROR_XPC_JS_THREW_STRING,
1462 77 : strBytes.ptr(), ifaceName, methodName,
1463 77 : nsnull, exceptn, cx, &s);
1464 : }
1465 : }
1466 0 : return NS_ERROR_FAILURE;
1467 : }
1468 :
1469 : /********************************/
1470 :
1471 : // static
1472 : nsresult
1473 256 : XPCConvert::JSErrorToXPCException(XPCCallContext& ccx,
1474 : const char* message,
1475 : const char* ifaceName,
1476 : const char* methodName,
1477 : const JSErrorReport* report,
1478 : nsIException** exceptn)
1479 : {
1480 256 : nsresult rv = NS_ERROR_FAILURE;
1481 512 : nsRefPtr<nsScriptError> data;
1482 256 : if (report) {
1483 512 : nsAutoString bestMessage;
1484 256 : if (report && report->ucmessage) {
1485 256 : bestMessage = (const PRUnichar *)report->ucmessage;
1486 0 : } else if (message) {
1487 0 : bestMessage.AssignWithConversion(message);
1488 : } else {
1489 0 : bestMessage.AssignLiteral("JavaScript Error");
1490 : }
1491 :
1492 256 : data = new nsScriptError();
1493 256 : if (!data)
1494 0 : return NS_ERROR_OUT_OF_MEMORY;
1495 :
1496 :
1497 256 : data->InitWithWindowID(bestMessage.get(),
1498 512 : NS_ConvertASCIItoUTF16(report->filename).get(),
1499 : (const PRUnichar *)report->uclinebuf, report->lineno,
1500 : report->uctokenptr - report->uclinebuf, report->flags,
1501 : "XPConnect JavaScript",
1502 256 : nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(ccx.GetJSContext()));
1503 : }
1504 :
1505 256 : if (data) {
1506 512 : nsCAutoString formattedMsg;
1507 256 : data->ToString(formattedMsg);
1508 :
1509 : rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS,
1510 : formattedMsg.get(), ifaceName, methodName,
1511 256 : static_cast<nsIScriptError*>(data.get()),
1512 256 : exceptn, nsnull, nsnull);
1513 : } else {
1514 : rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR,
1515 : nsnull, ifaceName, methodName, nsnull,
1516 0 : exceptn, nsnull, nsnull);
1517 : }
1518 256 : return rv;
1519 : }
1520 :
1521 : /***************************************************************************/
1522 :
1523 : // array fun...
1524 :
1525 : #ifdef POPULATE
1526 : #undef POPULATE
1527 : #endif
1528 :
1529 : // static
1530 : JSBool
1531 40181 : XPCConvert::NativeArray2JS(XPCLazyCallContext& lccx,
1532 : jsval* d, const void** s,
1533 : const nsXPTType& type, const nsID* iid,
1534 : uint32_t count, nsresult* pErr)
1535 : {
1536 40181 : NS_PRECONDITION(s, "bad param");
1537 40181 : NS_PRECONDITION(d, "bad param");
1538 :
1539 40181 : XPCCallContext& ccx = lccx.GetXPCCallContext();
1540 40181 : if (!ccx.IsValid())
1541 0 : return false;
1542 :
1543 40181 : JSContext* cx = ccx.GetJSContext();
1544 40181 : NS_ABORT_IF_FALSE(js::IsObjectInContextCompartment(lccx.GetScopeForNewJSObjects(), cx),
1545 : "bad scope for new JSObjects");
1546 :
1547 : // XXX add support for putting chars in a string rather than an array
1548 :
1549 : // XXX add support to indicate *which* array element was not convertable
1550 :
1551 40181 : JSObject *array = JS_NewArrayObject(cx, count, nsnull);
1552 :
1553 40181 : if (!array)
1554 0 : return false;
1555 :
1556 : // root this early
1557 40181 : *d = OBJECT_TO_JSVAL(array);
1558 80362 : AUTO_MARK_JSVAL(ccx, d);
1559 :
1560 40181 : if (pErr)
1561 40087 : *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
1562 :
1563 : uint32_t i;
1564 40181 : jsval current = JSVAL_NULL;
1565 80362 : AUTO_MARK_JSVAL(ccx, ¤t);
1566 :
1567 : #define POPULATE(_t) \
1568 : PR_BEGIN_MACRO \
1569 : for (i = 0; i < count; i++) { \
1570 : if (!NativeData2JS(ccx, ¤t, ((_t*)*s)+i, type, iid, pErr) ||\
1571 : !JS_SetElement(cx, array, i, ¤t)) \
1572 : goto failure; \
1573 : } \
1574 : PR_END_MACRO
1575 :
1576 : // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
1577 :
1578 40181 : switch (type.TagPart()) {
1579 0 : case nsXPTType::T_I8 : POPULATE(int8_t); break;
1580 12 : case nsXPTType::T_I16 : POPULATE(int16_t); break;
1581 0 : case nsXPTType::T_I32 : POPULATE(int32_t); break;
1582 8867 : case nsXPTType::T_I64 : POPULATE(int64_t); break;
1583 14828 : case nsXPTType::T_U8 : POPULATE(uint8_t); break;
1584 0 : case nsXPTType::T_U16 : POPULATE(uint16_t); break;
1585 8342 : case nsXPTType::T_U32 : POPULATE(uint32_t); break;
1586 0 : case nsXPTType::T_U64 : POPULATE(uint64_t); break;
1587 0 : case nsXPTType::T_FLOAT : POPULATE(float); break;
1588 12 : case nsXPTType::T_DOUBLE : POPULATE(double); break;
1589 0 : case nsXPTType::T_BOOL : POPULATE(bool); break;
1590 0 : case nsXPTType::T_CHAR : POPULATE(char); break;
1591 0 : case nsXPTType::T_WCHAR : POPULATE(jschar); break;
1592 0 : case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure;
1593 1 : case nsXPTType::T_IID : POPULATE(nsID*); break;
1594 0 : case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure;
1595 1152 : case nsXPTType::T_CHAR_STR : POPULATE(char*); break;
1596 1737 : case nsXPTType::T_WCHAR_STR : POPULATE(jschar*); break;
1597 5224 : case nsXPTType::T_INTERFACE : POPULATE(nsISupports*); break;
1598 6 : case nsXPTType::T_INTERFACE_IS : POPULATE(nsISupports*); break;
1599 0 : case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); goto failure;
1600 0 : case nsXPTType::T_CSTRING : NS_ERROR("bad type"); goto failure;
1601 0 : case nsXPTType::T_ASTRING : NS_ERROR("bad type"); goto failure;
1602 0 : default : NS_ERROR("bad type"); goto failure;
1603 : }
1604 :
1605 40181 : if (pErr)
1606 40087 : *pErr = NS_OK;
1607 40181 : return true;
1608 :
1609 : failure:
1610 0 : return false;
1611 :
1612 : #undef POPULATE
1613 : }
1614 :
1615 :
1616 :
1617 : // Check that the tag part of the type matches the type
1618 : // of the array. If the check succeeds, check that the size
1619 : // of the output does not exceed PR_UINT32_MAX bytes. Allocate
1620 : // the memory and copy the elements by memcpy.
1621 : static JSBool
1622 11 : CheckTargetAndPopulate(const nsXPTType& type,
1623 : PRUint8 requiredType,
1624 : size_t typeSize,
1625 : uint32_t count,
1626 : JSObject* tArr,
1627 : void** output,
1628 : nsresult* pErr)
1629 : {
1630 : // Check that the element type expected by the interface matches
1631 : // the type of the elements in the typed array exactly, including
1632 : // signedness.
1633 11 : if (type.TagPart() != requiredType) {
1634 2 : if (pErr)
1635 2 : *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1636 :
1637 2 : return false;
1638 : }
1639 :
1640 : // Calulate the maximum number of elements that can fit in
1641 : // PR_UINT32_MAX bytes.
1642 9 : size_t max = PR_UINT32_MAX / typeSize;
1643 :
1644 : // This could overflow on 32-bit systems so check max first.
1645 9 : size_t byteSize = count * typeSize;
1646 9 : if (count > max || !(*output = nsMemory::Alloc(byteSize))) {
1647 0 : if (pErr)
1648 0 : *pErr = NS_ERROR_OUT_OF_MEMORY;
1649 :
1650 0 : return false;
1651 : }
1652 :
1653 9 : memcpy(*output, JS_GetTypedArrayData(tArr), byteSize);
1654 9 : return true;
1655 : }
1656 :
1657 : // Fast conversion of typed arrays to native using memcpy.
1658 : // No float or double canonicalization is done. Called by
1659 : // JSarray2Native whenever a TypedArray is met. ArrayBuffers
1660 : // are not accepted; create a properly typed array view on them
1661 : // first. The element type of array must match the XPCOM
1662 : // type in size, type and signedness exactly. As an exception,
1663 : // Uint8ClampedArray is allowed for arrays of uint8_t.
1664 :
1665 : // static
1666 : JSBool
1667 13 : XPCConvert::JSTypedArray2Native(XPCCallContext& ccx,
1668 : void** d,
1669 : JSObject* jsArray,
1670 : uint32_t count,
1671 : const nsXPTType& type,
1672 : nsresult* pErr)
1673 : {
1674 13 : NS_ABORT_IF_FALSE(jsArray, "bad param");
1675 13 : NS_ABORT_IF_FALSE(d, "bad param");
1676 13 : NS_ABORT_IF_FALSE(js_IsTypedArray(jsArray), "not a typed array");
1677 :
1678 : // Check the actual length of the input array against the
1679 : // given size_is.
1680 13 : uint32_t len = JS_GetTypedArrayLength(jsArray);
1681 13 : if (len < count) {
1682 2 : if (pErr)
1683 2 : *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
1684 :
1685 2 : return false;
1686 : }
1687 :
1688 11 : void* output = nsnull;
1689 :
1690 11 : switch (JS_GetTypedArrayType(jsArray)) {
1691 : case js::TypedArray::TYPE_INT8:
1692 0 : if (!CheckTargetAndPopulate(nsXPTType::T_I8, type,
1693 : sizeof(int8_t), count,
1694 0 : jsArray, &output, pErr)) {
1695 0 : return false;
1696 : }
1697 0 : break;
1698 :
1699 : case js::TypedArray::TYPE_UINT8:
1700 : case js::TypedArray::TYPE_UINT8_CLAMPED:
1701 1 : if (!CheckTargetAndPopulate(nsXPTType::T_U8, type,
1702 : sizeof(uint8_t), count,
1703 1 : jsArray, &output, pErr)) {
1704 0 : return false;
1705 : }
1706 1 : break;
1707 :
1708 : case js::TypedArray::TYPE_INT16:
1709 4 : if (!CheckTargetAndPopulate(nsXPTType::T_I16, type,
1710 : sizeof(int16_t), count,
1711 4 : jsArray, &output, pErr)) {
1712 0 : return false;
1713 : }
1714 4 : break;
1715 :
1716 : case js::TypedArray::TYPE_UINT16:
1717 2 : if (!CheckTargetAndPopulate(nsXPTType::T_U16, type,
1718 : sizeof(uint16_t), count,
1719 2 : jsArray, &output, pErr)) {
1720 2 : return false;
1721 : }
1722 0 : break;
1723 :
1724 : case js::TypedArray::TYPE_INT32:
1725 0 : if (!CheckTargetAndPopulate(nsXPTType::T_I32, type,
1726 : sizeof(int32_t), count,
1727 0 : jsArray, &output, pErr)) {
1728 0 : return false;
1729 : }
1730 0 : break;
1731 :
1732 : case js::TypedArray::TYPE_UINT32:
1733 0 : if (!CheckTargetAndPopulate(nsXPTType::T_U32, type,
1734 : sizeof(uint32_t), count,
1735 0 : jsArray, &output, pErr)) {
1736 0 : return false;
1737 : }
1738 0 : break;
1739 :
1740 : case js::TypedArray::TYPE_FLOAT32:
1741 0 : if (!CheckTargetAndPopulate(nsXPTType::T_FLOAT, type,
1742 : sizeof(float), count,
1743 0 : jsArray, &output, pErr)) {
1744 0 : return false;
1745 : }
1746 0 : break;
1747 :
1748 : case js::TypedArray::TYPE_FLOAT64:
1749 4 : if (!CheckTargetAndPopulate(nsXPTType::T_DOUBLE, type,
1750 : sizeof(double), count,
1751 4 : jsArray, &output, pErr)) {
1752 0 : return false;
1753 : }
1754 4 : break;
1755 :
1756 : // Yet another array type was defined? It is not supported yet...
1757 : default:
1758 0 : if (pErr)
1759 0 : *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1760 :
1761 0 : return false;
1762 : }
1763 :
1764 9 : *d = output;
1765 9 : if (pErr)
1766 9 : *pErr = NS_OK;
1767 :
1768 9 : return true;
1769 : }
1770 :
1771 : // static
1772 : JSBool
1773 28525 : XPCConvert::JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
1774 : uint32_t count, const nsXPTType& type,
1775 : const nsID* iid, nsresult* pErr)
1776 : {
1777 28525 : NS_ABORT_IF_FALSE(d, "bad param");
1778 :
1779 28525 : JSContext* cx = ccx.GetJSContext();
1780 :
1781 : // No Action, FRee memory, RElease object
1782 : enum CleanupMode {na, fr, re};
1783 :
1784 : CleanupMode cleanupMode;
1785 :
1786 28525 : JSObject* jsarray = nsnull;
1787 28525 : void* array = nsnull;
1788 : uint32_t initedCount;
1789 : jsval current;
1790 :
1791 : // XXX add support for getting chars from strings
1792 :
1793 : // XXX add support to indicate *which* array element was not convertable
1794 :
1795 28525 : if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
1796 0 : if (0 != count) {
1797 0 : if (pErr)
1798 0 : *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
1799 0 : return false;
1800 : }
1801 :
1802 0 : *d = nsnull;
1803 0 : return true;
1804 : }
1805 :
1806 28525 : if (!JSVAL_IS_OBJECT(s)) {
1807 0 : if (pErr)
1808 0 : *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY;
1809 0 : return false;
1810 : }
1811 :
1812 28525 : jsarray = JSVAL_TO_OBJECT(s);
1813 :
1814 : // If this is a typed array, then do a fast conversion with memcpy.
1815 28525 : if (js_IsTypedArray(jsarray)) {
1816 13 : return JSTypedArray2Native(ccx, d, jsarray, count, type, pErr);
1817 : }
1818 :
1819 28512 : if (!JS_IsArrayObject(cx, jsarray)) {
1820 0 : if (pErr)
1821 0 : *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
1822 0 : return false;
1823 : }
1824 :
1825 : uint32_t len;
1826 28512 : if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
1827 0 : if (pErr)
1828 0 : *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
1829 0 : return false;
1830 : }
1831 :
1832 28512 : if (pErr)
1833 27613 : *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
1834 :
1835 : #define POPULATE(_mode, _t) \
1836 : PR_BEGIN_MACRO \
1837 : cleanupMode = _mode; \
1838 : size_t max = PR_UINT32_MAX / sizeof(_t); \
1839 : if (count > max || \
1840 : nsnull == (array = nsMemory::Alloc(count * sizeof(_t)))) { \
1841 : if (pErr) \
1842 : *pErr = NS_ERROR_OUT_OF_MEMORY; \
1843 : goto failure; \
1844 : } \
1845 : for (initedCount = 0; initedCount < count; initedCount++) { \
1846 : if (!JS_GetElement(cx, jsarray, initedCount, ¤t) || \
1847 : !JSData2Native(ccx, ((_t*)array)+initedCount, current, type, \
1848 : true, iid, pErr)) \
1849 : goto failure; \
1850 : } \
1851 : PR_END_MACRO
1852 :
1853 :
1854 : // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
1855 :
1856 : // XXX make extra space at end of char* and wchar* and null termintate
1857 :
1858 28512 : switch (type.TagPart()) {
1859 0 : case nsXPTType::T_I8 : POPULATE(na, int8_t); break;
1860 8 : case nsXPTType::T_I16 : POPULATE(na, int16_t); break;
1861 1 : case nsXPTType::T_I32 : POPULATE(na, int32_t); break;
1862 571 : case nsXPTType::T_I64 : POPULATE(na, int64_t); break;
1863 23256 : case nsXPTType::T_U8 : POPULATE(na, uint8_t); break;
1864 0 : case nsXPTType::T_U16 : POPULATE(na, uint16_t); break;
1865 85 : case nsXPTType::T_U32 : POPULATE(na, uint32_t); break;
1866 0 : case nsXPTType::T_U64 : POPULATE(na, uint64_t); break;
1867 0 : case nsXPTType::T_FLOAT : POPULATE(na, float); break;
1868 9 : case nsXPTType::T_DOUBLE : POPULATE(na, double); break;
1869 0 : case nsXPTType::T_BOOL : POPULATE(na, bool); break;
1870 0 : case nsXPTType::T_CHAR : POPULATE(na, char); break;
1871 0 : case nsXPTType::T_WCHAR : POPULATE(na, jschar); break;
1872 0 : case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure;
1873 37 : case nsXPTType::T_IID : POPULATE(fr, nsID*); break;
1874 0 : case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure;
1875 29 : case nsXPTType::T_CHAR_STR : POPULATE(fr, char*); break;
1876 779 : case nsXPTType::T_WCHAR_STR : POPULATE(fr, jschar*); break;
1877 3701 : case nsXPTType::T_INTERFACE : POPULATE(re, nsISupports*); break;
1878 36 : case nsXPTType::T_INTERFACE_IS : POPULATE(re, nsISupports*); break;
1879 0 : case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); goto failure;
1880 0 : case nsXPTType::T_CSTRING : NS_ERROR("bad type"); goto failure;
1881 0 : case nsXPTType::T_ASTRING : NS_ERROR("bad type"); goto failure;
1882 0 : default : NS_ERROR("bad type"); goto failure;
1883 : }
1884 :
1885 28512 : *d = array;
1886 28512 : if (pErr)
1887 27613 : *pErr = NS_OK;
1888 28512 : return true;
1889 :
1890 : failure:
1891 : // we may need to cleanup the partially filled array of converted stuff
1892 0 : if (array) {
1893 0 : if (cleanupMode == re) {
1894 0 : nsISupports** a = (nsISupports**) array;
1895 0 : for (PRUint32 i = 0; i < initedCount; i++) {
1896 0 : nsISupports* p = a[i];
1897 0 : NS_IF_RELEASE(p);
1898 : }
1899 0 : } else if (cleanupMode == fr) {
1900 0 : void** a = (void**) array;
1901 0 : for (PRUint32 i = 0; i < initedCount; i++) {
1902 0 : void* p = a[i];
1903 0 : if (p) nsMemory::Free(p);
1904 : }
1905 : }
1906 0 : nsMemory::Free(array);
1907 : }
1908 :
1909 0 : return false;
1910 :
1911 : #undef POPULATE
1912 : }
1913 :
1914 : // static
1915 : JSBool
1916 656 : XPCConvert::NativeStringWithSize2JS(JSContext* cx,
1917 : jsval* d, const void* s,
1918 : const nsXPTType& type,
1919 : uint32_t count,
1920 : nsresult* pErr)
1921 : {
1922 656 : NS_PRECONDITION(s, "bad param");
1923 656 : NS_PRECONDITION(d, "bad param");
1924 :
1925 656 : if (pErr)
1926 652 : *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
1927 :
1928 656 : switch (type.TagPart()) {
1929 : case nsXPTType::T_PSTRING_SIZE_IS:
1930 : {
1931 650 : char* p = *((char**)s);
1932 650 : if (!p)
1933 0 : break;
1934 : JSString* str;
1935 650 : if (!(str = JS_NewStringCopyN(cx, p, count)))
1936 0 : return false;
1937 650 : *d = STRING_TO_JSVAL(str);
1938 650 : break;
1939 : }
1940 : case nsXPTType::T_PWSTRING_SIZE_IS:
1941 : {
1942 6 : jschar* p = *((jschar**)s);
1943 6 : if (!p)
1944 0 : break;
1945 : JSString* str;
1946 6 : if (!(str = JS_NewUCStringCopyN(cx, p, count)))
1947 0 : return false;
1948 6 : *d = STRING_TO_JSVAL(str);
1949 6 : break;
1950 : }
1951 : default:
1952 0 : XPC_LOG_ERROR(("XPCConvert::NativeStringWithSize2JS : unsupported type"));
1953 0 : return false;
1954 : }
1955 656 : return true;
1956 : }
1957 :
1958 : // static
1959 : JSBool
1960 14 : XPCConvert::JSStringWithSize2Native(XPCCallContext& ccx, void* d, jsval s,
1961 : uint32_t count, const nsXPTType& type,
1962 : nsresult* pErr)
1963 : {
1964 14 : NS_PRECONDITION(!JSVAL_IS_NULL(s), "bad param");
1965 14 : NS_PRECONDITION(d, "bad param");
1966 :
1967 14 : JSContext* cx = ccx.GetJSContext();
1968 :
1969 : uint32_t len;
1970 :
1971 14 : if (pErr)
1972 10 : *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
1973 :
1974 14 : switch (type.TagPart()) {
1975 : case nsXPTType::T_PSTRING_SIZE_IS:
1976 : {
1977 8 : if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
1978 0 : if (0 != count) {
1979 0 : if (pErr)
1980 0 : *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
1981 0 : return false;
1982 : }
1983 0 : if (0 != count) {
1984 0 : len = (count + 1) * sizeof(char);
1985 0 : if (!(*((void**)d) = nsMemory::Alloc(len)))
1986 0 : return false;
1987 0 : return true;
1988 : }
1989 : // else ...
1990 :
1991 0 : *((char**)d) = nsnull;
1992 0 : return true;
1993 : }
1994 :
1995 8 : JSString* str = JS_ValueToString(cx, s);
1996 8 : if (!str) {
1997 0 : return false;
1998 : }
1999 :
2000 8 : size_t length = JS_GetStringEncodingLength(cx, str);
2001 8 : if (length == size_t(-1)) {
2002 0 : return false;
2003 : }
2004 8 : if (length > count) {
2005 0 : if (pErr)
2006 0 : *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
2007 0 : return false;
2008 : }
2009 8 : len = PRUint32(length);
2010 :
2011 8 : if (len < count)
2012 0 : len = count;
2013 :
2014 8 : uint32_t alloc_len = (len + 1) * sizeof(char);
2015 8 : char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
2016 8 : if (!buffer) {
2017 0 : return false;
2018 : }
2019 8 : JS_EncodeStringToBuffer(str, buffer, len);
2020 8 : buffer[len] = '\0';
2021 8 : *((char**)d) = buffer;
2022 :
2023 8 : return true;
2024 : }
2025 :
2026 : case nsXPTType::T_PWSTRING_SIZE_IS:
2027 : {
2028 6 : const jschar* chars=nsnull;
2029 : JSString* str;
2030 :
2031 6 : if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
2032 0 : if (0 != count) {
2033 0 : if (pErr)
2034 0 : *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
2035 0 : return false;
2036 : }
2037 :
2038 0 : if (0 != count) {
2039 0 : len = (count + 1) * sizeof(jschar);
2040 0 : if (!(*((void**)d) = nsMemory::Alloc(len)))
2041 0 : return false;
2042 0 : return true;
2043 : }
2044 :
2045 : // else ...
2046 0 : *((const jschar**)d) = nsnull;
2047 0 : return true;
2048 : }
2049 :
2050 6 : if (!(str = JS_ValueToString(cx, s))) {
2051 0 : return false;
2052 : }
2053 :
2054 6 : len = JS_GetStringLength(str);
2055 6 : if (len > count) {
2056 0 : if (pErr)
2057 0 : *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
2058 0 : return false;
2059 : }
2060 6 : if (len < count)
2061 0 : len = count;
2062 :
2063 6 : if (!(chars = JS_GetStringCharsZ(cx, str))) {
2064 0 : return false;
2065 : }
2066 6 : uint32_t alloc_len = (len + 1) * sizeof(jschar);
2067 6 : if (!(*((void**)d) = nsMemory::Alloc(alloc_len))) {
2068 : // XXX should report error
2069 0 : return false;
2070 : }
2071 6 : memcpy(*((jschar**)d), chars, alloc_len);
2072 6 : (*((jschar**)d))[count] = 0;
2073 :
2074 6 : return true;
2075 : }
2076 : default:
2077 0 : XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type"));
2078 0 : return false;
2079 : }
2080 : }
2081 :
|