|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */ |
|
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 /* Data conversion between native and JavaScript types. */ |
|
8 |
|
9 #include "mozilla/ArrayUtils.h" |
|
10 |
|
11 #include "xpcprivate.h" |
|
12 #include "nsIAtom.h" |
|
13 #include "nsWrapperCache.h" |
|
14 #include "nsJSUtils.h" |
|
15 #include "WrapperFactory.h" |
|
16 |
|
17 #include "nsWrapperCacheInlines.h" |
|
18 |
|
19 #include "jsapi.h" |
|
20 #include "jsfriendapi.h" |
|
21 #include "jsprf.h" |
|
22 #include "JavaScriptParent.h" |
|
23 |
|
24 #include "mozilla/dom/BindingUtils.h" |
|
25 #include "mozilla/dom/DOMException.h" |
|
26 #include "mozilla/dom/PrimitiveConversions.h" |
|
27 |
|
28 using namespace xpc; |
|
29 using namespace mozilla; |
|
30 using namespace mozilla::dom; |
|
31 using namespace JS; |
|
32 |
|
33 //#define STRICT_CHECK_OF_UNICODE |
|
34 #ifdef STRICT_CHECK_OF_UNICODE |
|
35 #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF80)) |
|
36 #else // STRICT_CHECK_OF_UNICODE |
|
37 #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF00)) |
|
38 #endif // STRICT_CHECK_OF_UNICODE |
|
39 |
|
40 #define ILLEGAL_CHAR_RANGE(c) (0!=((c) & 0x80)) |
|
41 |
|
42 /***********************************************************/ |
|
43 |
|
44 // static |
|
45 bool |
|
46 XPCConvert::IsMethodReflectable(const XPTMethodDescriptor& info) |
|
47 { |
|
48 if (XPT_MD_IS_NOTXPCOM(info.flags) || XPT_MD_IS_HIDDEN(info.flags)) |
|
49 return false; |
|
50 |
|
51 for (int i = info.num_args-1; i >= 0; i--) { |
|
52 const nsXPTParamInfo& param = info.params[i]; |
|
53 const nsXPTType& type = param.GetType(); |
|
54 |
|
55 // Reflected methods can't use native types. All native types end up |
|
56 // getting tagged as void*, so this check is easy. |
|
57 if (type.TagPart() == nsXPTType::T_VOID) |
|
58 return false; |
|
59 } |
|
60 return true; |
|
61 } |
|
62 |
|
63 static JSObject* |
|
64 UnwrapNativeCPOW(nsISupports* wrapper) |
|
65 { |
|
66 nsCOMPtr<nsIXPConnectWrappedJS> underware = do_QueryInterface(wrapper); |
|
67 if (underware) { |
|
68 JSObject* mainObj = underware->GetJSObject(); |
|
69 if (mainObj && mozilla::jsipc::JavaScriptParent::IsCPOW(mainObj)) |
|
70 return mainObj; |
|
71 } |
|
72 return nullptr; |
|
73 } |
|
74 |
|
75 /***************************************************************************/ |
|
76 |
|
77 // static |
|
78 bool |
|
79 XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface) |
|
80 { |
|
81 const JSClass* jsclass = js::GetObjectJSClass(obj); |
|
82 MOZ_ASSERT(jsclass, "obj has no class"); |
|
83 if (jsclass && |
|
84 (jsclass->flags & JSCLASS_HAS_PRIVATE) && |
|
85 (jsclass->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)) { |
|
86 *iface = (nsISupports*) xpc_GetJSPrivate(obj); |
|
87 return true; |
|
88 } |
|
89 *iface = UnwrapDOMObjectToISupports(obj); |
|
90 return !!*iface; |
|
91 } |
|
92 |
|
93 /***************************************************************************/ |
|
94 |
|
95 // static |
|
96 bool |
|
97 XPCConvert::NativeData2JS(MutableHandleValue d, const void* s, |
|
98 const nsXPTType& type, const nsID* iid, nsresult* pErr) |
|
99 { |
|
100 NS_PRECONDITION(s, "bad param"); |
|
101 |
|
102 AutoJSContext cx; |
|
103 if (pErr) |
|
104 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; |
|
105 |
|
106 switch (type.TagPart()) { |
|
107 case nsXPTType::T_I8 : |
|
108 d.setInt32(*static_cast<const int8_t*>(s)); |
|
109 return true; |
|
110 case nsXPTType::T_I16 : |
|
111 d.setInt32(*static_cast<const int16_t*>(s)); |
|
112 return true; |
|
113 case nsXPTType::T_I32 : |
|
114 d.setInt32(*static_cast<const int32_t*>(s)); |
|
115 return true; |
|
116 case nsXPTType::T_I64 : |
|
117 d.setNumber(static_cast<double>(*static_cast<const int64_t*>(s))); |
|
118 return true; |
|
119 case nsXPTType::T_U8 : |
|
120 d.setInt32(*static_cast<const uint8_t*>(s)); |
|
121 return true; |
|
122 case nsXPTType::T_U16 : |
|
123 d.setInt32(*static_cast<const uint16_t*>(s)); |
|
124 return true; |
|
125 case nsXPTType::T_U32 : |
|
126 d.setNumber(*static_cast<const uint32_t*>(s)); |
|
127 return true; |
|
128 case nsXPTType::T_U64 : |
|
129 d.setNumber(static_cast<double>(*static_cast<const uint64_t*>(s))); |
|
130 return true; |
|
131 case nsXPTType::T_FLOAT : |
|
132 d.setNumber(*static_cast<const float*>(s)); |
|
133 return true; |
|
134 case nsXPTType::T_DOUBLE: |
|
135 d.setNumber(*static_cast<const double*>(s)); |
|
136 return true; |
|
137 case nsXPTType::T_BOOL : |
|
138 d.setBoolean(*static_cast<const bool*>(s)); |
|
139 return true; |
|
140 case nsXPTType::T_CHAR : |
|
141 { |
|
142 char p = *static_cast<const char*>(s); |
|
143 |
|
144 #ifdef STRICT_CHECK_OF_UNICODE |
|
145 MOZ_ASSERT(! ILLEGAL_CHAR_RANGE(p) , "passing non ASCII data"); |
|
146 #endif // STRICT_CHECK_OF_UNICODE |
|
147 |
|
148 JSString* str = JS_NewStringCopyN(cx, &p, 1); |
|
149 if (!str) |
|
150 return false; |
|
151 |
|
152 d.setString(str); |
|
153 return true; |
|
154 } |
|
155 case nsXPTType::T_WCHAR : |
|
156 { |
|
157 jschar p = *static_cast<const jschar*>(s); |
|
158 |
|
159 JSString* str = JS_NewUCStringCopyN(cx, &p, 1); |
|
160 if (!str) |
|
161 return false; |
|
162 |
|
163 d.setString(str); |
|
164 return true; |
|
165 } |
|
166 |
|
167 case nsXPTType::T_JSVAL : |
|
168 { |
|
169 d.set(*static_cast<const Value*>(s)); |
|
170 return JS_WrapValue(cx, d); |
|
171 } |
|
172 |
|
173 case nsXPTType::T_VOID: |
|
174 XPC_LOG_ERROR(("XPCConvert::NativeData2JS : void* params not supported")); |
|
175 return false; |
|
176 |
|
177 case nsXPTType::T_IID: |
|
178 { |
|
179 nsID* iid2 = *static_cast<nsID* const *>(s); |
|
180 if (!iid2) { |
|
181 d.setNull(); |
|
182 return true; |
|
183 } |
|
184 |
|
185 RootedObject scope(cx, JS::CurrentGlobalOrNull(cx)); |
|
186 JSObject* obj = xpc_NewIDObject(cx, scope, *iid2); |
|
187 if (!obj) |
|
188 return false; |
|
189 |
|
190 d.setObject(*obj); |
|
191 return true; |
|
192 } |
|
193 |
|
194 case nsXPTType::T_ASTRING: |
|
195 // Fall through to T_DOMSTRING case |
|
196 |
|
197 case nsXPTType::T_DOMSTRING: |
|
198 { |
|
199 const nsAString* p = *static_cast<const nsAString* const *>(s); |
|
200 if (!p || p->IsVoid()) { |
|
201 d.setNull(); |
|
202 return true; |
|
203 } |
|
204 |
|
205 nsStringBuffer* buf; |
|
206 if (!XPCStringConvert::ReadableToJSVal(cx, *p, &buf, d)) |
|
207 return false; |
|
208 if (buf) |
|
209 buf->AddRef(); |
|
210 return true; |
|
211 } |
|
212 |
|
213 case nsXPTType::T_CHAR_STR: |
|
214 { |
|
215 const char* p = *static_cast<const char* const *>(s); |
|
216 if (!p) { |
|
217 d.setNull(); |
|
218 return true; |
|
219 } |
|
220 |
|
221 #ifdef STRICT_CHECK_OF_UNICODE |
|
222 bool isAscii = true; |
|
223 for (char* t = p; *t && isAscii; t++) { |
|
224 if (ILLEGAL_CHAR_RANGE(*t)) |
|
225 isAscii = false; |
|
226 } |
|
227 MOZ_ASSERT(isAscii, "passing non ASCII data"); |
|
228 #endif // STRICT_CHECK_OF_UNICODE |
|
229 |
|
230 JSString* str = JS_NewStringCopyZ(cx, p); |
|
231 if (!str) |
|
232 return false; |
|
233 |
|
234 d.setString(str); |
|
235 return true; |
|
236 } |
|
237 |
|
238 case nsXPTType::T_WCHAR_STR: |
|
239 { |
|
240 const jschar* p = *static_cast<const jschar* const *>(s); |
|
241 if (!p) { |
|
242 d.setNull(); |
|
243 return true; |
|
244 } |
|
245 |
|
246 JSString* str = JS_NewUCStringCopyZ(cx, p); |
|
247 if (!str) |
|
248 return false; |
|
249 |
|
250 d.setString(str); |
|
251 return true; |
|
252 } |
|
253 case nsXPTType::T_UTF8STRING: |
|
254 { |
|
255 const nsACString* utf8String = *static_cast<const nsACString* const *>(s); |
|
256 |
|
257 if (!utf8String || utf8String->IsVoid()) { |
|
258 d.setNull(); |
|
259 return true; |
|
260 } |
|
261 |
|
262 if (utf8String->IsEmpty()) { |
|
263 d.set(JS_GetEmptyStringValue(cx)); |
|
264 return true; |
|
265 } |
|
266 |
|
267 const uint32_t len = CalcUTF8ToUnicodeLength(*utf8String); |
|
268 // The cString is not empty at this point, but the calculated |
|
269 // UTF-16 length is zero, meaning no valid conversion exists. |
|
270 if (!len) |
|
271 return false; |
|
272 |
|
273 const size_t buffer_size = (len + 1) * sizeof(char16_t); |
|
274 char16_t* buffer = |
|
275 static_cast<char16_t*>(JS_malloc(cx, buffer_size)); |
|
276 if (!buffer) |
|
277 return false; |
|
278 |
|
279 uint32_t copied; |
|
280 if (!UTF8ToUnicodeBuffer(*utf8String, buffer, &copied) || |
|
281 len != copied) { |
|
282 // Copy or conversion during copy failed. Did not copy the |
|
283 // whole string. |
|
284 JS_free(cx, buffer); |
|
285 return false; |
|
286 } |
|
287 |
|
288 // JS_NewUCString takes ownership on success, i.e. a |
|
289 // successful call will make it the responsiblity of the JS VM |
|
290 // to free the buffer. |
|
291 JSString* str = JS_NewUCString(cx, buffer, len); |
|
292 if (!str) { |
|
293 JS_free(cx, buffer); |
|
294 return false; |
|
295 } |
|
296 |
|
297 d.setString(str); |
|
298 return true; |
|
299 } |
|
300 case nsXPTType::T_CSTRING: |
|
301 { |
|
302 const nsACString* cString = *static_cast<const nsACString* const *>(s); |
|
303 |
|
304 if (!cString || cString->IsVoid()) { |
|
305 d.setNull(); |
|
306 return true; |
|
307 } |
|
308 |
|
309 // c-strings (binary blobs) are deliberately not converted from |
|
310 // UTF-8 to UTF-16. T_UTF8Sting is for UTF-8 encoded strings |
|
311 // with automatic conversion. |
|
312 JSString* str = JS_NewStringCopyN(cx, cString->Data(), |
|
313 cString->Length()); |
|
314 if (!str) |
|
315 return false; |
|
316 |
|
317 d.setString(str); |
|
318 return true; |
|
319 } |
|
320 |
|
321 case nsXPTType::T_INTERFACE: |
|
322 case nsXPTType::T_INTERFACE_IS: |
|
323 { |
|
324 nsISupports* iface = *static_cast<nsISupports* const *>(s); |
|
325 if (!iface) { |
|
326 d.setNull(); |
|
327 return true; |
|
328 } |
|
329 |
|
330 if (iid->Equals(NS_GET_IID(nsIVariant))) { |
|
331 nsCOMPtr<nsIVariant> variant = do_QueryInterface(iface); |
|
332 if (!variant) |
|
333 return false; |
|
334 |
|
335 return XPCVariant::VariantDataToJS(variant, |
|
336 pErr, d); |
|
337 } |
|
338 |
|
339 xpcObjectHelper helper(iface); |
|
340 return NativeInterface2JSObject(d, nullptr, helper, iid, nullptr, true, pErr); |
|
341 } |
|
342 |
|
343 default: |
|
344 NS_ERROR("bad type"); |
|
345 return false; |
|
346 } |
|
347 return true; |
|
348 } |
|
349 |
|
350 /***************************************************************************/ |
|
351 |
|
352 #ifdef DEBUG |
|
353 static bool |
|
354 CheckJSCharInCharRange(jschar c) |
|
355 { |
|
356 if (ILLEGAL_RANGE(c)) { |
|
357 /* U+0080/U+0100 - U+FFFF data lost. */ |
|
358 static const size_t MSG_BUF_SIZE = 64; |
|
359 char msg[MSG_BUF_SIZE]; |
|
360 JS_snprintf(msg, MSG_BUF_SIZE, "jschar out of char range; high bits of data lost: 0x%x", c); |
|
361 NS_WARNING(msg); |
|
362 return false; |
|
363 } |
|
364 |
|
365 return true; |
|
366 } |
|
367 #endif |
|
368 |
|
369 template<typename T> |
|
370 bool ConvertToPrimitive(JSContext *cx, HandleValue v, T *retval) |
|
371 { |
|
372 return ValueToPrimitive<T, eDefault>(cx, v, retval); |
|
373 } |
|
374 |
|
375 // static |
|
376 bool |
|
377 XPCConvert::JSData2Native(void* d, HandleValue s, |
|
378 const nsXPTType& type, |
|
379 bool useAllocator, const nsID* iid, |
|
380 nsresult* pErr) |
|
381 { |
|
382 NS_PRECONDITION(d, "bad param"); |
|
383 |
|
384 AutoJSContext cx; |
|
385 if (pErr) |
|
386 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; |
|
387 |
|
388 switch (type.TagPart()) { |
|
389 case nsXPTType::T_I8 : |
|
390 return ConvertToPrimitive(cx, s, static_cast<int8_t*>(d)); |
|
391 case nsXPTType::T_I16 : |
|
392 return ConvertToPrimitive(cx, s, static_cast<int16_t*>(d)); |
|
393 case nsXPTType::T_I32 : |
|
394 return ConvertToPrimitive(cx, s, static_cast<int32_t*>(d)); |
|
395 case nsXPTType::T_I64 : |
|
396 return ConvertToPrimitive(cx, s, static_cast<int64_t*>(d)); |
|
397 case nsXPTType::T_U8 : |
|
398 return ConvertToPrimitive(cx, s, static_cast<uint8_t*>(d)); |
|
399 case nsXPTType::T_U16 : |
|
400 return ConvertToPrimitive(cx, s, static_cast<uint16_t*>(d)); |
|
401 case nsXPTType::T_U32 : |
|
402 return ConvertToPrimitive(cx, s, static_cast<uint32_t*>(d)); |
|
403 case nsXPTType::T_U64 : |
|
404 return ConvertToPrimitive(cx, s, static_cast<uint64_t*>(d)); |
|
405 case nsXPTType::T_FLOAT : |
|
406 return ConvertToPrimitive(cx, s, static_cast<float*>(d)); |
|
407 case nsXPTType::T_DOUBLE : |
|
408 return ConvertToPrimitive(cx, s, static_cast<double*>(d)); |
|
409 case nsXPTType::T_BOOL : |
|
410 return ConvertToPrimitive(cx, s, static_cast<bool*>(d)); |
|
411 case nsXPTType::T_CHAR : |
|
412 { |
|
413 JSString* str = ToString(cx, s); |
|
414 if (!str) { |
|
415 return false; |
|
416 } |
|
417 size_t length; |
|
418 const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); |
|
419 if (!chars) { |
|
420 return false; |
|
421 } |
|
422 jschar ch = length ? chars[0] : 0; |
|
423 #ifdef DEBUG |
|
424 CheckJSCharInCharRange(ch); |
|
425 #endif |
|
426 *((char*)d) = char(ch); |
|
427 break; |
|
428 } |
|
429 case nsXPTType::T_WCHAR : |
|
430 { |
|
431 JSString* str; |
|
432 if (!(str = ToString(cx, s))) { |
|
433 return false; |
|
434 } |
|
435 size_t length; |
|
436 const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); |
|
437 if (!chars) { |
|
438 return false; |
|
439 } |
|
440 if (length == 0) { |
|
441 *((uint16_t*)d) = 0; |
|
442 break; |
|
443 } |
|
444 *((uint16_t*)d) = uint16_t(chars[0]); |
|
445 break; |
|
446 } |
|
447 case nsXPTType::T_JSVAL : |
|
448 *((jsval*)d) = s; |
|
449 break; |
|
450 case nsXPTType::T_VOID: |
|
451 XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported")); |
|
452 NS_ERROR("void* params not supported"); |
|
453 return false; |
|
454 case nsXPTType::T_IID: |
|
455 { |
|
456 const nsID* pid = nullptr; |
|
457 |
|
458 // There's no good reason to pass a null IID. |
|
459 if (s.isNullOrUndefined()) { |
|
460 if (pErr) |
|
461 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; |
|
462 return false; |
|
463 } |
|
464 |
|
465 if (!s.isObject() || |
|
466 (!(pid = xpc_JSObjectToID(cx, &s.toObject()))) || |
|
467 (!(pid = (const nsID*) nsMemory::Clone(pid, sizeof(nsID))))) { |
|
468 return false; |
|
469 } |
|
470 *((const nsID**)d) = pid; |
|
471 return true; |
|
472 } |
|
473 |
|
474 case nsXPTType::T_ASTRING: |
|
475 { |
|
476 if (JSVAL_IS_VOID(s)) { |
|
477 if (useAllocator) |
|
478 *((const nsAString**)d) = &NullString(); |
|
479 else |
|
480 (**((nsAString**)d)).SetIsVoid(true); |
|
481 return true; |
|
482 } |
|
483 // Fall through to T_DOMSTRING case. |
|
484 } |
|
485 case nsXPTType::T_DOMSTRING: |
|
486 { |
|
487 if (JSVAL_IS_NULL(s)) { |
|
488 if (useAllocator) |
|
489 *((const nsAString**)d) = &NullString(); |
|
490 else |
|
491 (**((nsAString**)d)).SetIsVoid(true); |
|
492 return true; |
|
493 } |
|
494 size_t length = 0; |
|
495 const char16_t* chars = nullptr; |
|
496 JSString* str = nullptr; |
|
497 if (!JSVAL_IS_VOID(s)) { |
|
498 str = ToString(cx, s); |
|
499 if (!str) |
|
500 return false; |
|
501 |
|
502 chars = useAllocator ? JS_GetStringCharsZAndLength(cx, str, &length) |
|
503 : JS_GetStringCharsAndLength(cx, str, &length); |
|
504 if (!chars) |
|
505 return false; |
|
506 |
|
507 if (!length) { |
|
508 if (useAllocator) |
|
509 *((const nsAString**)d) = &EmptyString(); |
|
510 else |
|
511 (**((nsAString**)d)).Truncate(); |
|
512 return true; |
|
513 } |
|
514 } |
|
515 |
|
516 nsString* ws; |
|
517 if (useAllocator) { |
|
518 ws = nsXPConnect::GetRuntimeInstance()->NewShortLivedString(); |
|
519 *((const nsString**)d) = ws; |
|
520 } else { |
|
521 ws = *((nsString**)d); |
|
522 } |
|
523 |
|
524 if (!str) { |
|
525 ws->AssignLiteral(MOZ_UTF16("undefined")); |
|
526 } else if (XPCStringConvert::IsDOMString(str)) { |
|
527 // The characters represent an existing nsStringBuffer that |
|
528 // was shared by XPCStringConvert::ReadableToJSVal. |
|
529 nsStringBuffer::FromData((void *)chars)->ToString(length, *ws); |
|
530 } else if (XPCStringConvert::IsLiteral(str)) { |
|
531 // The characters represent a literal char16_t string constant |
|
532 // compiled into libxul, such as the string "undefined" above. |
|
533 ws->AssignLiteral(chars, length); |
|
534 } else if (useAllocator && STRING_TO_JSVAL(str) == s) { |
|
535 // The JS string will exist over the function call. |
|
536 // We don't need to copy the characters in this case. |
|
537 ws->Rebind(chars, length); |
|
538 } else { |
|
539 ws->Assign(chars, length); |
|
540 } |
|
541 return true; |
|
542 } |
|
543 |
|
544 case nsXPTType::T_CHAR_STR: |
|
545 { |
|
546 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) { |
|
547 *((char**)d) = nullptr; |
|
548 return true; |
|
549 } |
|
550 |
|
551 JSString* str = ToString(cx, s); |
|
552 if (!str) { |
|
553 return false; |
|
554 } |
|
555 #ifdef DEBUG |
|
556 const jschar* chars=nullptr; |
|
557 if (nullptr != (chars = JS_GetStringCharsZ(cx, str))) { |
|
558 bool legalRange = true; |
|
559 int len = JS_GetStringLength(str); |
|
560 const jschar* t; |
|
561 int32_t i=0; |
|
562 for (t=chars; (i< len) && legalRange ; i++,t++) { |
|
563 if (!CheckJSCharInCharRange(*t)) |
|
564 break; |
|
565 } |
|
566 } |
|
567 #endif // DEBUG |
|
568 size_t length = JS_GetStringEncodingLength(cx, str); |
|
569 if (length == size_t(-1)) { |
|
570 return false; |
|
571 } |
|
572 char *buffer = static_cast<char *>(nsMemory::Alloc(length + 1)); |
|
573 if (!buffer) { |
|
574 return false; |
|
575 } |
|
576 JS_EncodeStringToBuffer(cx, str, buffer, length); |
|
577 buffer[length] = '\0'; |
|
578 *((void**)d) = buffer; |
|
579 return true; |
|
580 } |
|
581 |
|
582 case nsXPTType::T_WCHAR_STR: |
|
583 { |
|
584 const jschar* chars=nullptr; |
|
585 JSString* str; |
|
586 |
|
587 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) { |
|
588 *((jschar**)d) = nullptr; |
|
589 return true; |
|
590 } |
|
591 |
|
592 if (!(str = ToString(cx, s))) { |
|
593 return false; |
|
594 } |
|
595 if (!(chars = JS_GetStringCharsZ(cx, str))) { |
|
596 return false; |
|
597 } |
|
598 int len = JS_GetStringLength(str); |
|
599 int byte_len = (len+1)*sizeof(jschar); |
|
600 if (!(*((void**)d) = nsMemory::Alloc(byte_len))) { |
|
601 // XXX should report error |
|
602 return false; |
|
603 } |
|
604 jschar* destchars = *((jschar**)d); |
|
605 memcpy(destchars, chars, byte_len); |
|
606 destchars[len] = 0; |
|
607 |
|
608 return true; |
|
609 } |
|
610 |
|
611 case nsXPTType::T_UTF8STRING: |
|
612 { |
|
613 const jschar* chars; |
|
614 size_t length; |
|
615 JSString* str; |
|
616 |
|
617 if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) { |
|
618 if (useAllocator) { |
|
619 *((const nsACString**)d) = &NullCString(); |
|
620 } else { |
|
621 nsCString* rs = *((nsCString**)d); |
|
622 rs->SetIsVoid(true); |
|
623 } |
|
624 return true; |
|
625 } |
|
626 |
|
627 // The JS val is neither null nor void... |
|
628 |
|
629 if (!(str = ToString(cx, s))|| |
|
630 !(chars = JS_GetStringCharsAndLength(cx, str, &length))) { |
|
631 return false; |
|
632 } |
|
633 |
|
634 if (!length) { |
|
635 if (useAllocator) { |
|
636 *((const nsACString**)d) = &EmptyCString(); |
|
637 } else { |
|
638 nsCString* rs = *((nsCString**)d); |
|
639 rs->Truncate(); |
|
640 } |
|
641 return true; |
|
642 } |
|
643 |
|
644 nsCString *rs; |
|
645 if (useAllocator) { |
|
646 // Use nsCString to enable sharing |
|
647 rs = new nsCString(); |
|
648 if (!rs) |
|
649 return false; |
|
650 |
|
651 *((const nsCString**)d) = rs; |
|
652 } else { |
|
653 rs = *((nsCString**)d); |
|
654 } |
|
655 CopyUTF16toUTF8(Substring(chars, length), *rs); |
|
656 return true; |
|
657 } |
|
658 |
|
659 case nsXPTType::T_CSTRING: |
|
660 { |
|
661 if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) { |
|
662 if (useAllocator) { |
|
663 nsACString *rs = new nsCString(); |
|
664 if (!rs) |
|
665 return false; |
|
666 |
|
667 rs->SetIsVoid(true); |
|
668 *((nsACString**)d) = rs; |
|
669 } else { |
|
670 nsACString* rs = *((nsACString**)d); |
|
671 rs->Truncate(); |
|
672 rs->SetIsVoid(true); |
|
673 } |
|
674 return true; |
|
675 } |
|
676 |
|
677 // The JS val is neither null nor void... |
|
678 JSString* str = ToString(cx, s); |
|
679 if (!str) { |
|
680 return false; |
|
681 } |
|
682 |
|
683 size_t length = JS_GetStringEncodingLength(cx, str); |
|
684 if (length == size_t(-1)) { |
|
685 return false; |
|
686 } |
|
687 |
|
688 if (!length) { |
|
689 if (useAllocator) { |
|
690 *((const nsACString**)d) = &EmptyCString(); |
|
691 } else { |
|
692 nsCString* rs = *((nsCString**)d); |
|
693 rs->Truncate(); |
|
694 } |
|
695 return true; |
|
696 } |
|
697 |
|
698 nsACString *rs; |
|
699 if (useAllocator) { |
|
700 rs = new nsCString(); |
|
701 if (!rs) |
|
702 return false; |
|
703 *((const nsACString**)d) = rs; |
|
704 } else { |
|
705 rs = *((nsACString**)d); |
|
706 } |
|
707 |
|
708 rs->SetLength(uint32_t(length)); |
|
709 if (rs->Length() != uint32_t(length)) { |
|
710 return false; |
|
711 } |
|
712 JS_EncodeStringToBuffer(cx, str, rs->BeginWriting(), length); |
|
713 |
|
714 return true; |
|
715 } |
|
716 |
|
717 case nsXPTType::T_INTERFACE: |
|
718 case nsXPTType::T_INTERFACE_IS: |
|
719 { |
|
720 MOZ_ASSERT(iid,"can't do interface conversions without iid"); |
|
721 |
|
722 if (iid->Equals(NS_GET_IID(nsIVariant))) { |
|
723 nsCOMPtr<nsIVariant> variant = XPCVariant::newVariant(cx, s); |
|
724 if (!variant) |
|
725 return false; |
|
726 |
|
727 variant.forget(static_cast<nsISupports**>(d)); |
|
728 return true; |
|
729 } else if (iid->Equals(NS_GET_IID(nsIAtom)) && |
|
730 JSVAL_IS_STRING(s)) { |
|
731 // We're trying to pass a string as an nsIAtom. Let's atomize! |
|
732 JSString* str = JSVAL_TO_STRING(s); |
|
733 const char16_t* chars = JS_GetStringCharsZ(cx, str); |
|
734 if (!chars) { |
|
735 if (pErr) |
|
736 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF; |
|
737 return false; |
|
738 } |
|
739 uint32_t length = JS_GetStringLength(str); |
|
740 nsCOMPtr<nsIAtom> atom = |
|
741 NS_NewAtom(nsDependentSubstring(chars, chars + length)); |
|
742 atom.forget((nsISupports**)d); |
|
743 return true; |
|
744 } |
|
745 //else ... |
|
746 |
|
747 if (s.isNullOrUndefined()) { |
|
748 *((nsISupports**)d) = nullptr; |
|
749 return true; |
|
750 } |
|
751 |
|
752 // only wrap JSObjects |
|
753 if (!s.isObject()) { |
|
754 if (pErr && s.isInt32() && 0 == s.toInt32()) |
|
755 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL; |
|
756 return false; |
|
757 } |
|
758 |
|
759 RootedObject src(cx, &s.toObject()); |
|
760 return JSObject2NativeInterface((void**)d, src, iid, nullptr, pErr); |
|
761 } |
|
762 default: |
|
763 NS_ERROR("bad type"); |
|
764 return false; |
|
765 } |
|
766 return true; |
|
767 } |
|
768 |
|
769 static inline bool |
|
770 CreateHolderIfNeeded(HandleObject obj, MutableHandleValue d, |
|
771 nsIXPConnectJSObjectHolder** dest) |
|
772 { |
|
773 if (dest) { |
|
774 nsRefPtr<XPCJSObjectHolder> objHolder = XPCJSObjectHolder::newHolder(obj); |
|
775 if (!objHolder) |
|
776 return false; |
|
777 |
|
778 objHolder.forget(dest); |
|
779 } |
|
780 |
|
781 d.setObjectOrNull(obj); |
|
782 |
|
783 return true; |
|
784 } |
|
785 |
|
786 /***************************************************************************/ |
|
787 // static |
|
788 bool |
|
789 XPCConvert::NativeInterface2JSObject(MutableHandleValue d, |
|
790 nsIXPConnectJSObjectHolder** dest, |
|
791 xpcObjectHelper& aHelper, |
|
792 const nsID* iid, |
|
793 XPCNativeInterface** Interface, |
|
794 bool allowNativeWrapper, |
|
795 nsresult* pErr) |
|
796 { |
|
797 MOZ_ASSERT_IF(Interface, iid); |
|
798 if (!iid) |
|
799 iid = &NS_GET_IID(nsISupports); |
|
800 |
|
801 d.setNull(); |
|
802 if (dest) |
|
803 *dest = nullptr; |
|
804 if (!aHelper.Object()) |
|
805 return true; |
|
806 if (pErr) |
|
807 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; |
|
808 |
|
809 // We used to have code here that unwrapped and simply exposed the |
|
810 // underlying JSObject. That caused anomolies when JSComponents were |
|
811 // accessed from other JS code - they didn't act like other xpconnect |
|
812 // wrapped components. So, instead, we create "double wrapped" objects |
|
813 // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't |
|
814 // optimal -- we could detect this and roll the functionality into a |
|
815 // single wrapper, but the current solution is good enough for now. |
|
816 AutoJSContext cx; |
|
817 XPCWrappedNativeScope* xpcscope = GetObjectScope(JS::CurrentGlobalOrNull(cx)); |
|
818 if (!xpcscope) |
|
819 return false; |
|
820 |
|
821 // First, see if this object supports the wrapper cache. |
|
822 // Note: If |cache->IsDOMBinding()| is true, then it means that the object |
|
823 // implementing it doesn't want a wrapped native as its JS Object, but |
|
824 // instead it provides its own proxy object. In that case, the object |
|
825 // to use is found as cache->GetWrapper(). If that is null, then the |
|
826 // object will create (and fill the cache) from its WrapObject call. |
|
827 nsWrapperCache *cache = aHelper.GetWrapperCache(); |
|
828 |
|
829 RootedObject flat(cx, cache ? cache->GetWrapper() : nullptr); |
|
830 if (!flat && cache && cache->IsDOMBinding()) { |
|
831 RootedObject global(cx, xpcscope->GetGlobalJSObject()); |
|
832 js::AssertSameCompartment(cx, global); |
|
833 flat = cache->WrapObject(cx); |
|
834 if (!flat) |
|
835 return false; |
|
836 } |
|
837 if (flat) { |
|
838 if (allowNativeWrapper && !JS_WrapObject(cx, &flat)) |
|
839 return false; |
|
840 return CreateHolderIfNeeded(flat, d, dest); |
|
841 } |
|
842 |
|
843 // Don't double wrap CPOWs. This is a temporary measure for compatibility |
|
844 // with objects that don't provide necessary QIs (such as objects under |
|
845 // the new DOM bindings). We expect the other side of the CPOW to have |
|
846 // the appropriate wrappers in place. |
|
847 RootedObject cpow(cx, UnwrapNativeCPOW(aHelper.Object())); |
|
848 if (cpow) { |
|
849 if (!JS_WrapObject(cx, &cpow)) |
|
850 return false; |
|
851 d.setObject(*cpow); |
|
852 return true; |
|
853 } |
|
854 |
|
855 // We can't simply construct a slim wrapper. Go ahead and create an |
|
856 // XPCWrappedNative for this object. At this point, |flat| could be |
|
857 // non-null, meaning that either we already have a wrapped native from |
|
858 // the cache (which might need to be QI'd to the new interface) or that |
|
859 // we found a slim wrapper that we'll have to morph. |
|
860 AutoMarkingNativeInterfacePtr iface(cx); |
|
861 if (iid) { |
|
862 if (Interface) |
|
863 iface = *Interface; |
|
864 |
|
865 if (!iface) { |
|
866 iface = XPCNativeInterface::GetNewOrUsed(iid); |
|
867 if (!iface) |
|
868 return false; |
|
869 |
|
870 if (Interface) |
|
871 *Interface = iface; |
|
872 } |
|
873 } |
|
874 |
|
875 MOZ_ASSERT(!flat || IS_WN_REFLECTOR(flat), "What kind of wrapper is this?"); |
|
876 |
|
877 nsresult rv; |
|
878 XPCWrappedNative* wrapper; |
|
879 nsRefPtr<XPCWrappedNative> strongWrapper; |
|
880 if (!flat) { |
|
881 rv = XPCWrappedNative::GetNewOrUsed(aHelper, xpcscope, iface, |
|
882 getter_AddRefs(strongWrapper)); |
|
883 |
|
884 wrapper = strongWrapper; |
|
885 } else { |
|
886 MOZ_ASSERT(IS_WN_REFLECTOR(flat)); |
|
887 |
|
888 wrapper = XPCWrappedNative::Get(flat); |
|
889 |
|
890 // If asked to return the wrapper we'll return a strong reference, |
|
891 // otherwise we'll just return its JSObject in d (which should be |
|
892 // rooted in that case). |
|
893 if (dest) |
|
894 strongWrapper = wrapper; |
|
895 if (iface) |
|
896 wrapper->FindTearOff(iface, false, &rv); |
|
897 else |
|
898 rv = NS_OK; |
|
899 } |
|
900 |
|
901 if (NS_FAILED(rv) && pErr) |
|
902 *pErr = rv; |
|
903 |
|
904 // If creating the wrapped native failed, then return early. |
|
905 if (NS_FAILED(rv) || !wrapper) |
|
906 return false; |
|
907 |
|
908 // If we're not creating security wrappers, we can return the |
|
909 // XPCWrappedNative as-is here. |
|
910 flat = wrapper->GetFlatJSObject(); |
|
911 jsval v = OBJECT_TO_JSVAL(flat); |
|
912 if (!allowNativeWrapper) { |
|
913 d.set(v); |
|
914 if (dest) |
|
915 strongWrapper.forget(dest); |
|
916 if (pErr) |
|
917 *pErr = NS_OK; |
|
918 return true; |
|
919 } |
|
920 |
|
921 // The call to wrap here handles both cross-compartment and same-compartment |
|
922 // security wrappers. |
|
923 RootedObject original(cx, flat); |
|
924 if (!JS_WrapObject(cx, &flat)) |
|
925 return false; |
|
926 |
|
927 d.setObjectOrNull(flat); |
|
928 |
|
929 if (dest) { |
|
930 // The strongWrapper still holds the original flat object. |
|
931 if (flat == original) { |
|
932 strongWrapper.forget(dest); |
|
933 } else { |
|
934 nsRefPtr<XPCJSObjectHolder> objHolder = |
|
935 XPCJSObjectHolder::newHolder(flat); |
|
936 if (!objHolder) |
|
937 return false; |
|
938 |
|
939 objHolder.forget(dest); |
|
940 } |
|
941 } |
|
942 |
|
943 if (pErr) |
|
944 *pErr = NS_OK; |
|
945 |
|
946 return true; |
|
947 } |
|
948 |
|
949 /***************************************************************************/ |
|
950 |
|
951 // static |
|
952 bool |
|
953 XPCConvert::JSObject2NativeInterface(void** dest, HandleObject src, |
|
954 const nsID* iid, |
|
955 nsISupports* aOuter, |
|
956 nsresult* pErr) |
|
957 { |
|
958 MOZ_ASSERT(dest, "bad param"); |
|
959 MOZ_ASSERT(src, "bad param"); |
|
960 MOZ_ASSERT(iid, "bad param"); |
|
961 |
|
962 AutoJSContext cx; |
|
963 JSAutoCompartment ac(cx, src); |
|
964 |
|
965 *dest = nullptr; |
|
966 if (pErr) |
|
967 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; |
|
968 |
|
969 nsISupports* iface; |
|
970 |
|
971 if (!aOuter) { |
|
972 // Note that if we have a non-null aOuter then it means that we are |
|
973 // forcing the creation of a wrapper even if the object *is* a |
|
974 // wrappedNative or other wise has 'nsISupportness'. |
|
975 // This allows wrapJSAggregatedToNative to work. |
|
976 |
|
977 // If we're looking at a security wrapper, see now if we're allowed to |
|
978 // pass it to C++. If we are, then fall through to the code below. If |
|
979 // we aren't, throw an exception eagerly. |
|
980 // |
|
981 // NB: It's very important that we _don't_ unwrap in the aOuter case, |
|
982 // because the caller may explicitly want to create the XPCWrappedJS |
|
983 // around a security wrapper. XBL does this with Xrays from the XBL |
|
984 // scope - see nsBindingManager::GetBindingImplementation. |
|
985 JSObject* inner = js::CheckedUnwrap(src, /* stopAtOuter = */ false); |
|
986 |
|
987 // Hack - For historical reasons, wrapped chrome JS objects have been |
|
988 // passable as native interfaces. We'd like to fix this, but it |
|
989 // involves fixing the contacts API and PeerConnection to stop using |
|
990 // COWs. This needs to happen, but for now just preserve the old |
|
991 // behavior. |
|
992 // |
|
993 // Note that there is an identical hack in getWrapper which should be |
|
994 // removed if this one is. |
|
995 if (!inner && MOZ_UNLIKELY(xpc::WrapperFactory::IsCOW(src))) |
|
996 inner = js::UncheckedUnwrap(src); |
|
997 if (!inner) { |
|
998 if (pErr) |
|
999 *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO; |
|
1000 return false; |
|
1001 } |
|
1002 |
|
1003 // Is this really a native xpcom object with a wrapper? |
|
1004 XPCWrappedNative* wrappedNative = nullptr; |
|
1005 if (IS_WN_REFLECTOR(inner)) |
|
1006 wrappedNative = XPCWrappedNative::Get(inner); |
|
1007 if (wrappedNative) { |
|
1008 iface = wrappedNative->GetIdentityObject(); |
|
1009 return NS_SUCCEEDED(iface->QueryInterface(*iid, dest)); |
|
1010 } |
|
1011 // else... |
|
1012 |
|
1013 // Deal with slim wrappers here. |
|
1014 if (GetISupportsFromJSObject(inner ? inner : src, &iface)) { |
|
1015 if (iface) |
|
1016 return NS_SUCCEEDED(iface->QueryInterface(*iid, dest)); |
|
1017 |
|
1018 return false; |
|
1019 } |
|
1020 } |
|
1021 |
|
1022 // else... |
|
1023 |
|
1024 nsXPCWrappedJS* wrapper; |
|
1025 nsresult rv = nsXPCWrappedJS::GetNewOrUsed(src, *iid, &wrapper); |
|
1026 if (pErr) |
|
1027 *pErr = rv; |
|
1028 if (NS_SUCCEEDED(rv) && wrapper) { |
|
1029 // If the caller wanted to aggregate this JS object to a native, |
|
1030 // attach it to the wrapper. Note that we allow a maximum of one |
|
1031 // aggregated native for a given XPCWrappedJS. |
|
1032 if (aOuter) |
|
1033 wrapper->SetAggregatedNativeObject(aOuter); |
|
1034 |
|
1035 // We need to go through the QueryInterface logic to make this return |
|
1036 // the right thing for the various 'special' interfaces; e.g. |
|
1037 // nsIPropertyBag. We must use AggregatedQueryInterface in cases where |
|
1038 // there is an outer to avoid nasty recursion. |
|
1039 rv = aOuter ? wrapper->AggregatedQueryInterface(*iid, dest) : |
|
1040 wrapper->QueryInterface(*iid, dest); |
|
1041 if (pErr) |
|
1042 *pErr = rv; |
|
1043 NS_RELEASE(wrapper); |
|
1044 return NS_SUCCEEDED(rv); |
|
1045 } |
|
1046 |
|
1047 // else... |
|
1048 return false; |
|
1049 } |
|
1050 |
|
1051 /***************************************************************************/ |
|
1052 /***************************************************************************/ |
|
1053 |
|
1054 // static |
|
1055 nsresult |
|
1056 XPCConvert::ConstructException(nsresult rv, const char* message, |
|
1057 const char* ifaceName, const char* methodName, |
|
1058 nsISupports* data, |
|
1059 nsIException** exceptn, |
|
1060 JSContext* cx, |
|
1061 jsval* jsExceptionPtr) |
|
1062 { |
|
1063 MOZ_ASSERT(!cx == !jsExceptionPtr, "Expected cx and jsExceptionPtr to cooccur."); |
|
1064 |
|
1065 static const char format[] = "\'%s\' when calling method: [%s::%s]"; |
|
1066 const char * msg = message; |
|
1067 nsXPIDLString xmsg; |
|
1068 nsAutoCString sxmsg; |
|
1069 |
|
1070 nsCOMPtr<nsIScriptError> errorObject = do_QueryInterface(data); |
|
1071 if (errorObject) { |
|
1072 if (NS_SUCCEEDED(errorObject->GetMessageMoz(getter_Copies(xmsg)))) { |
|
1073 CopyUTF16toUTF8(xmsg, sxmsg); |
|
1074 msg = sxmsg.get(); |
|
1075 } |
|
1076 } |
|
1077 if (!msg) |
|
1078 if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &msg) || ! msg) |
|
1079 msg = "<error>"; |
|
1080 |
|
1081 nsCString msgStr(msg); |
|
1082 if (ifaceName && methodName) |
|
1083 msgStr.AppendPrintf(format, msg, ifaceName, methodName); |
|
1084 |
|
1085 nsRefPtr<Exception> e = new Exception(msgStr, rv, EmptyCString(), nullptr, data); |
|
1086 |
|
1087 if (cx && jsExceptionPtr) { |
|
1088 e->StowJSVal(*jsExceptionPtr); |
|
1089 } |
|
1090 |
|
1091 e.forget(exceptn); |
|
1092 return NS_OK; |
|
1093 } |
|
1094 |
|
1095 /********************************/ |
|
1096 |
|
1097 class MOZ_STACK_CLASS AutoExceptionRestorer |
|
1098 { |
|
1099 public: |
|
1100 AutoExceptionRestorer(JSContext *cx, Value v) |
|
1101 : mContext(cx), tvr(cx, v) |
|
1102 { |
|
1103 JS_ClearPendingException(mContext); |
|
1104 } |
|
1105 |
|
1106 ~AutoExceptionRestorer() |
|
1107 { |
|
1108 JS_SetPendingException(mContext, tvr); |
|
1109 } |
|
1110 |
|
1111 private: |
|
1112 JSContext * const mContext; |
|
1113 RootedValue tvr; |
|
1114 }; |
|
1115 |
|
1116 // static |
|
1117 nsresult |
|
1118 XPCConvert::JSValToXPCException(MutableHandleValue s, |
|
1119 const char* ifaceName, |
|
1120 const char* methodName, |
|
1121 nsIException** exceptn) |
|
1122 { |
|
1123 AutoJSContext cx; |
|
1124 AutoExceptionRestorer aer(cx, s); |
|
1125 |
|
1126 if (!JSVAL_IS_PRIMITIVE(s)) { |
|
1127 // we have a JSObject |
|
1128 RootedObject obj(cx, JSVAL_TO_OBJECT(s)); |
|
1129 |
|
1130 if (!obj) { |
|
1131 NS_ERROR("when is an object not an object?"); |
|
1132 return NS_ERROR_FAILURE; |
|
1133 } |
|
1134 |
|
1135 // is this really a native xpcom object with a wrapper? |
|
1136 JSObject *unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false); |
|
1137 if (!unwrapped) |
|
1138 return NS_ERROR_XPC_SECURITY_MANAGER_VETO; |
|
1139 XPCWrappedNative* wrapper = IS_WN_REFLECTOR(unwrapped) ? XPCWrappedNative::Get(unwrapped) |
|
1140 : nullptr; |
|
1141 if (wrapper) { |
|
1142 nsISupports* supports = wrapper->GetIdentityObject(); |
|
1143 nsCOMPtr<nsIException> iface = do_QueryInterface(supports); |
|
1144 if (iface) { |
|
1145 // just pass through the exception (with extra ref and all) |
|
1146 nsCOMPtr<nsIException> temp = iface; |
|
1147 temp.forget(exceptn); |
|
1148 return NS_OK; |
|
1149 } else { |
|
1150 // it is a wrapped native, but not an exception! |
|
1151 return ConstructException(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT, |
|
1152 nullptr, ifaceName, methodName, supports, |
|
1153 exceptn, nullptr, nullptr); |
|
1154 } |
|
1155 } else { |
|
1156 // It is a JSObject, but not a wrapped native... |
|
1157 |
|
1158 // If it is an engine Error with an error report then let's |
|
1159 // extract the report and build an xpcexception from that |
|
1160 const JSErrorReport* report; |
|
1161 if (nullptr != (report = JS_ErrorFromException(cx, obj))) { |
|
1162 JSAutoByteString message; |
|
1163 JSString* str; |
|
1164 if (nullptr != (str = ToString(cx, s))) |
|
1165 message.encodeLatin1(cx, str); |
|
1166 return JSErrorToXPCException(message.ptr(), ifaceName, |
|
1167 methodName, report, exceptn); |
|
1168 } |
|
1169 |
|
1170 |
|
1171 bool found; |
|
1172 |
|
1173 // heuristic to see if it might be usable as an xpcexception |
|
1174 if (!JS_HasProperty(cx, obj, "message", &found)) |
|
1175 return NS_ERROR_FAILURE; |
|
1176 |
|
1177 if (found && !JS_HasProperty(cx, obj, "result", &found)) |
|
1178 return NS_ERROR_FAILURE; |
|
1179 |
|
1180 if (found) { |
|
1181 // lets try to build a wrapper around the JSObject |
|
1182 nsXPCWrappedJS* jswrapper; |
|
1183 nsresult rv = |
|
1184 nsXPCWrappedJS::GetNewOrUsed(obj, NS_GET_IID(nsIException), &jswrapper); |
|
1185 if (NS_FAILED(rv)) |
|
1186 return rv; |
|
1187 |
|
1188 *exceptn = static_cast<nsIException *>(jswrapper->GetXPTCStub()); |
|
1189 return NS_OK; |
|
1190 } |
|
1191 |
|
1192 |
|
1193 // XXX we should do a check against 'js_ErrorClass' here and |
|
1194 // do the right thing - even though it has no JSErrorReport, |
|
1195 // The fact that it is a JSError exceptions means we can extract |
|
1196 // particular info and our 'result' should reflect that. |
|
1197 |
|
1198 // otherwise we'll just try to convert it to a string |
|
1199 |
|
1200 JSString* str = ToString(cx, s); |
|
1201 if (!str) |
|
1202 return NS_ERROR_FAILURE; |
|
1203 |
|
1204 JSAutoByteString strBytes(cx, str); |
|
1205 if (!strBytes) |
|
1206 return NS_ERROR_FAILURE; |
|
1207 |
|
1208 return ConstructException(NS_ERROR_XPC_JS_THREW_JS_OBJECT, |
|
1209 strBytes.ptr(), ifaceName, methodName, |
|
1210 nullptr, exceptn, cx, s.address()); |
|
1211 } |
|
1212 } |
|
1213 |
|
1214 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) { |
|
1215 return ConstructException(NS_ERROR_XPC_JS_THREW_NULL, |
|
1216 nullptr, ifaceName, methodName, nullptr, |
|
1217 exceptn, cx, s.address()); |
|
1218 } |
|
1219 |
|
1220 if (JSVAL_IS_NUMBER(s)) { |
|
1221 // lets see if it looks like an nsresult |
|
1222 nsresult rv; |
|
1223 double number; |
|
1224 bool isResult = false; |
|
1225 |
|
1226 if (JSVAL_IS_INT(s)) { |
|
1227 rv = (nsresult) JSVAL_TO_INT(s); |
|
1228 if (NS_FAILED(rv)) |
|
1229 isResult = true; |
|
1230 else |
|
1231 number = (double) JSVAL_TO_INT(s); |
|
1232 } else { |
|
1233 number = JSVAL_TO_DOUBLE(s); |
|
1234 if (number > 0.0 && |
|
1235 number < (double)0xffffffff && |
|
1236 0.0 == fmod(number,1)) { |
|
1237 // Visual Studio 9 doesn't allow casting directly from a |
|
1238 // double to an enumeration type, contrary to 5.2.9(10) of |
|
1239 // C++11, so add an intermediate cast. |
|
1240 rv = (nsresult)(uint32_t) number; |
|
1241 if (NS_FAILED(rv)) |
|
1242 isResult = true; |
|
1243 } |
|
1244 } |
|
1245 |
|
1246 if (isResult) |
|
1247 return ConstructException(rv, nullptr, ifaceName, methodName, |
|
1248 nullptr, exceptn, cx, s.address()); |
|
1249 else { |
|
1250 // XXX all this nsISupportsDouble code seems a little redundant |
|
1251 // now that we're storing the jsval in the exception... |
|
1252 nsISupportsDouble* data; |
|
1253 nsCOMPtr<nsIComponentManager> cm; |
|
1254 if (NS_FAILED(NS_GetComponentManager(getter_AddRefs(cm))) || !cm || |
|
1255 NS_FAILED(cm->CreateInstanceByContractID(NS_SUPPORTS_DOUBLE_CONTRACTID, |
|
1256 nullptr, |
|
1257 NS_GET_IID(nsISupportsDouble), |
|
1258 (void**)&data))) |
|
1259 return NS_ERROR_FAILURE; |
|
1260 data->SetData(number); |
|
1261 rv = ConstructException(NS_ERROR_XPC_JS_THREW_NUMBER, nullptr, |
|
1262 ifaceName, methodName, data, exceptn, cx, s.address()); |
|
1263 NS_RELEASE(data); |
|
1264 return rv; |
|
1265 } |
|
1266 } |
|
1267 |
|
1268 // otherwise we'll just try to convert it to a string |
|
1269 // Note: e.g., bools get converted to JSStrings by this code. |
|
1270 |
|
1271 JSString* str = ToString(cx, s); |
|
1272 if (str) { |
|
1273 JSAutoByteString strBytes(cx, str); |
|
1274 if (!!strBytes) { |
|
1275 return ConstructException(NS_ERROR_XPC_JS_THREW_STRING, |
|
1276 strBytes.ptr(), ifaceName, methodName, |
|
1277 nullptr, exceptn, cx, s.address()); |
|
1278 } |
|
1279 } |
|
1280 return NS_ERROR_FAILURE; |
|
1281 } |
|
1282 |
|
1283 /********************************/ |
|
1284 |
|
1285 // static |
|
1286 nsresult |
|
1287 XPCConvert::JSErrorToXPCException(const char* message, |
|
1288 const char* ifaceName, |
|
1289 const char* methodName, |
|
1290 const JSErrorReport* report, |
|
1291 nsIException** exceptn) |
|
1292 { |
|
1293 AutoJSContext cx; |
|
1294 nsresult rv = NS_ERROR_FAILURE; |
|
1295 nsRefPtr<nsScriptError> data; |
|
1296 if (report) { |
|
1297 nsAutoString bestMessage; |
|
1298 if (report && report->ucmessage) { |
|
1299 bestMessage = static_cast<const char16_t*>(report->ucmessage); |
|
1300 } else if (message) { |
|
1301 CopyASCIItoUTF16(message, bestMessage); |
|
1302 } else { |
|
1303 bestMessage.AssignLiteral("JavaScript Error"); |
|
1304 } |
|
1305 |
|
1306 const char16_t* uclinebuf = |
|
1307 static_cast<const char16_t*>(report->uclinebuf); |
|
1308 |
|
1309 data = new nsScriptError(); |
|
1310 data->InitWithWindowID( |
|
1311 bestMessage, |
|
1312 NS_ConvertASCIItoUTF16(report->filename), |
|
1313 uclinebuf ? nsDependentString(uclinebuf) : EmptyString(), |
|
1314 report->lineno, |
|
1315 report->uctokenptr - report->uclinebuf, report->flags, |
|
1316 NS_LITERAL_CSTRING("XPConnect JavaScript"), |
|
1317 nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx)); |
|
1318 } |
|
1319 |
|
1320 if (data) { |
|
1321 nsAutoCString formattedMsg; |
|
1322 data->ToString(formattedMsg); |
|
1323 |
|
1324 rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS, |
|
1325 formattedMsg.get(), ifaceName, methodName, |
|
1326 static_cast<nsIScriptError*>(data.get()), |
|
1327 exceptn, nullptr, nullptr); |
|
1328 } else { |
|
1329 rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR, |
|
1330 nullptr, ifaceName, methodName, nullptr, |
|
1331 exceptn, nullptr, nullptr); |
|
1332 } |
|
1333 return rv; |
|
1334 } |
|
1335 |
|
1336 /***************************************************************************/ |
|
1337 |
|
1338 // array fun... |
|
1339 |
|
1340 #ifdef POPULATE |
|
1341 #undef POPULATE |
|
1342 #endif |
|
1343 |
|
1344 // static |
|
1345 bool |
|
1346 XPCConvert::NativeArray2JS(MutableHandleValue d, const void** s, |
|
1347 const nsXPTType& type, const nsID* iid, |
|
1348 uint32_t count, nsresult* pErr) |
|
1349 { |
|
1350 NS_PRECONDITION(s, "bad param"); |
|
1351 |
|
1352 AutoJSContext cx; |
|
1353 |
|
1354 // XXX add support for putting chars in a string rather than an array |
|
1355 |
|
1356 // XXX add support to indicate *which* array element was not convertable |
|
1357 |
|
1358 RootedObject array(cx, JS_NewArrayObject(cx, count)); |
|
1359 if (!array) |
|
1360 return false; |
|
1361 |
|
1362 if (pErr) |
|
1363 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; |
|
1364 |
|
1365 uint32_t i; |
|
1366 RootedValue current(cx, JSVAL_NULL); |
|
1367 |
|
1368 #define POPULATE(_t) \ |
|
1369 PR_BEGIN_MACRO \ |
|
1370 for (i = 0; i < count; i++) { \ |
|
1371 if (!NativeData2JS(¤t, ((_t*)*s)+i, type, iid, pErr) || \ |
|
1372 !JS_SetElement(cx, array, i, current)) \ |
|
1373 goto failure; \ |
|
1374 } \ |
|
1375 PR_END_MACRO |
|
1376 |
|
1377 // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*) |
|
1378 |
|
1379 switch (type.TagPart()) { |
|
1380 case nsXPTType::T_I8 : POPULATE(int8_t); break; |
|
1381 case nsXPTType::T_I16 : POPULATE(int16_t); break; |
|
1382 case nsXPTType::T_I32 : POPULATE(int32_t); break; |
|
1383 case nsXPTType::T_I64 : POPULATE(int64_t); break; |
|
1384 case nsXPTType::T_U8 : POPULATE(uint8_t); break; |
|
1385 case nsXPTType::T_U16 : POPULATE(uint16_t); break; |
|
1386 case nsXPTType::T_U32 : POPULATE(uint32_t); break; |
|
1387 case nsXPTType::T_U64 : POPULATE(uint64_t); break; |
|
1388 case nsXPTType::T_FLOAT : POPULATE(float); break; |
|
1389 case nsXPTType::T_DOUBLE : POPULATE(double); break; |
|
1390 case nsXPTType::T_BOOL : POPULATE(bool); break; |
|
1391 case nsXPTType::T_CHAR : POPULATE(char); break; |
|
1392 case nsXPTType::T_WCHAR : POPULATE(jschar); break; |
|
1393 case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure; |
|
1394 case nsXPTType::T_IID : POPULATE(nsID*); break; |
|
1395 case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure; |
|
1396 case nsXPTType::T_CHAR_STR : POPULATE(char*); break; |
|
1397 case nsXPTType::T_WCHAR_STR : POPULATE(jschar*); break; |
|
1398 case nsXPTType::T_INTERFACE : POPULATE(nsISupports*); break; |
|
1399 case nsXPTType::T_INTERFACE_IS : POPULATE(nsISupports*); break; |
|
1400 case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); goto failure; |
|
1401 case nsXPTType::T_CSTRING : NS_ERROR("bad type"); goto failure; |
|
1402 case nsXPTType::T_ASTRING : NS_ERROR("bad type"); goto failure; |
|
1403 default : NS_ERROR("bad type"); goto failure; |
|
1404 } |
|
1405 |
|
1406 if (pErr) |
|
1407 *pErr = NS_OK; |
|
1408 d.setObject(*array); |
|
1409 return true; |
|
1410 |
|
1411 failure: |
|
1412 return false; |
|
1413 |
|
1414 #undef POPULATE |
|
1415 } |
|
1416 |
|
1417 |
|
1418 |
|
1419 // Check that the tag part of the type matches the type |
|
1420 // of the array. If the check succeeds, check that the size |
|
1421 // of the output does not exceed UINT32_MAX bytes. Allocate |
|
1422 // the memory and copy the elements by memcpy. |
|
1423 static bool |
|
1424 CheckTargetAndPopulate(const nsXPTType& type, |
|
1425 uint8_t requiredType, |
|
1426 size_t typeSize, |
|
1427 uint32_t count, |
|
1428 JSObject* tArr, |
|
1429 void** output, |
|
1430 nsresult* pErr) |
|
1431 { |
|
1432 // Check that the element type expected by the interface matches |
|
1433 // the type of the elements in the typed array exactly, including |
|
1434 // signedness. |
|
1435 if (type.TagPart() != requiredType) { |
|
1436 if (pErr) |
|
1437 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; |
|
1438 |
|
1439 return false; |
|
1440 } |
|
1441 |
|
1442 // Calulate the maximum number of elements that can fit in |
|
1443 // UINT32_MAX bytes. |
|
1444 size_t max = UINT32_MAX / typeSize; |
|
1445 |
|
1446 // This could overflow on 32-bit systems so check max first. |
|
1447 size_t byteSize = count * typeSize; |
|
1448 if (count > max || !(*output = nsMemory::Alloc(byteSize))) { |
|
1449 if (pErr) |
|
1450 *pErr = NS_ERROR_OUT_OF_MEMORY; |
|
1451 |
|
1452 return false; |
|
1453 } |
|
1454 |
|
1455 memcpy(*output, JS_GetArrayBufferViewData(tArr), byteSize); |
|
1456 return true; |
|
1457 } |
|
1458 |
|
1459 // Fast conversion of typed arrays to native using memcpy. |
|
1460 // No float or double canonicalization is done. Called by |
|
1461 // JSarray2Native whenever a TypedArray is met. ArrayBuffers |
|
1462 // are not accepted; create a properly typed array view on them |
|
1463 // first. The element type of array must match the XPCOM |
|
1464 // type in size, type and signedness exactly. As an exception, |
|
1465 // Uint8ClampedArray is allowed for arrays of uint8_t. DataViews |
|
1466 // are not supported. |
|
1467 |
|
1468 // static |
|
1469 bool |
|
1470 XPCConvert::JSTypedArray2Native(void** d, |
|
1471 JSObject* jsArray, |
|
1472 uint32_t count, |
|
1473 const nsXPTType& type, |
|
1474 nsresult* pErr) |
|
1475 { |
|
1476 MOZ_ASSERT(jsArray, "bad param"); |
|
1477 MOZ_ASSERT(d, "bad param"); |
|
1478 MOZ_ASSERT(JS_IsTypedArrayObject(jsArray), "not a typed array"); |
|
1479 |
|
1480 // Check the actual length of the input array against the |
|
1481 // given size_is. |
|
1482 uint32_t len = JS_GetTypedArrayLength(jsArray); |
|
1483 if (len < count) { |
|
1484 if (pErr) |
|
1485 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY; |
|
1486 |
|
1487 return false; |
|
1488 } |
|
1489 |
|
1490 void* output = nullptr; |
|
1491 |
|
1492 switch (JS_GetArrayBufferViewType(jsArray)) { |
|
1493 case js::ArrayBufferView::TYPE_INT8: |
|
1494 if (!CheckTargetAndPopulate(nsXPTType::T_I8, type, |
|
1495 sizeof(int8_t), count, |
|
1496 jsArray, &output, pErr)) { |
|
1497 return false; |
|
1498 } |
|
1499 break; |
|
1500 |
|
1501 case js::ArrayBufferView::TYPE_UINT8: |
|
1502 case js::ArrayBufferView::TYPE_UINT8_CLAMPED: |
|
1503 if (!CheckTargetAndPopulate(nsXPTType::T_U8, type, |
|
1504 sizeof(uint8_t), count, |
|
1505 jsArray, &output, pErr)) { |
|
1506 return false; |
|
1507 } |
|
1508 break; |
|
1509 |
|
1510 case js::ArrayBufferView::TYPE_INT16: |
|
1511 if (!CheckTargetAndPopulate(nsXPTType::T_I16, type, |
|
1512 sizeof(int16_t), count, |
|
1513 jsArray, &output, pErr)) { |
|
1514 return false; |
|
1515 } |
|
1516 break; |
|
1517 |
|
1518 case js::ArrayBufferView::TYPE_UINT16: |
|
1519 if (!CheckTargetAndPopulate(nsXPTType::T_U16, type, |
|
1520 sizeof(uint16_t), count, |
|
1521 jsArray, &output, pErr)) { |
|
1522 return false; |
|
1523 } |
|
1524 break; |
|
1525 |
|
1526 case js::ArrayBufferView::TYPE_INT32: |
|
1527 if (!CheckTargetAndPopulate(nsXPTType::T_I32, type, |
|
1528 sizeof(int32_t), count, |
|
1529 jsArray, &output, pErr)) { |
|
1530 return false; |
|
1531 } |
|
1532 break; |
|
1533 |
|
1534 case js::ArrayBufferView::TYPE_UINT32: |
|
1535 if (!CheckTargetAndPopulate(nsXPTType::T_U32, type, |
|
1536 sizeof(uint32_t), count, |
|
1537 jsArray, &output, pErr)) { |
|
1538 return false; |
|
1539 } |
|
1540 break; |
|
1541 |
|
1542 case js::ArrayBufferView::TYPE_FLOAT32: |
|
1543 if (!CheckTargetAndPopulate(nsXPTType::T_FLOAT, type, |
|
1544 sizeof(float), count, |
|
1545 jsArray, &output, pErr)) { |
|
1546 return false; |
|
1547 } |
|
1548 break; |
|
1549 |
|
1550 case js::ArrayBufferView::TYPE_FLOAT64: |
|
1551 if (!CheckTargetAndPopulate(nsXPTType::T_DOUBLE, type, |
|
1552 sizeof(double), count, |
|
1553 jsArray, &output, pErr)) { |
|
1554 return false; |
|
1555 } |
|
1556 break; |
|
1557 |
|
1558 // Yet another array type was defined? It is not supported yet... |
|
1559 default: |
|
1560 if (pErr) |
|
1561 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; |
|
1562 |
|
1563 return false; |
|
1564 } |
|
1565 |
|
1566 *d = output; |
|
1567 if (pErr) |
|
1568 *pErr = NS_OK; |
|
1569 |
|
1570 return true; |
|
1571 } |
|
1572 |
|
1573 // static |
|
1574 bool |
|
1575 XPCConvert::JSArray2Native(void** d, HandleValue s, |
|
1576 uint32_t count, const nsXPTType& type, |
|
1577 const nsID* iid, nsresult* pErr) |
|
1578 { |
|
1579 MOZ_ASSERT(d, "bad param"); |
|
1580 |
|
1581 AutoJSContext cx; |
|
1582 |
|
1583 // XXX add support for getting chars from strings |
|
1584 |
|
1585 // XXX add support to indicate *which* array element was not convertable |
|
1586 |
|
1587 if (s.isNullOrUndefined()) { |
|
1588 if (0 != count) { |
|
1589 if (pErr) |
|
1590 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY; |
|
1591 return false; |
|
1592 } |
|
1593 |
|
1594 *d = nullptr; |
|
1595 return true; |
|
1596 } |
|
1597 |
|
1598 if (!s.isObject()) { |
|
1599 if (pErr) |
|
1600 *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY; |
|
1601 return false; |
|
1602 } |
|
1603 |
|
1604 RootedObject jsarray(cx, &s.toObject()); |
|
1605 |
|
1606 // If this is a typed array, then try a fast conversion with memcpy. |
|
1607 if (JS_IsTypedArrayObject(jsarray)) { |
|
1608 return JSTypedArray2Native(d, jsarray, count, type, pErr); |
|
1609 } |
|
1610 |
|
1611 if (!JS_IsArrayObject(cx, jsarray)) { |
|
1612 if (pErr) |
|
1613 *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY; |
|
1614 return false; |
|
1615 } |
|
1616 |
|
1617 uint32_t len; |
|
1618 if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) { |
|
1619 if (pErr) |
|
1620 *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY; |
|
1621 return false; |
|
1622 } |
|
1623 |
|
1624 if (pErr) |
|
1625 *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; |
|
1626 |
|
1627 #define POPULATE(_mode, _t) \ |
|
1628 PR_BEGIN_MACRO \ |
|
1629 cleanupMode = _mode; \ |
|
1630 size_t max = UINT32_MAX / sizeof(_t); \ |
|
1631 if (count > max || \ |
|
1632 nullptr == (array = nsMemory::Alloc(count * sizeof(_t)))) { \ |
|
1633 if (pErr) \ |
|
1634 *pErr = NS_ERROR_OUT_OF_MEMORY; \ |
|
1635 goto failure; \ |
|
1636 } \ |
|
1637 for (initedCount = 0; initedCount < count; initedCount++) { \ |
|
1638 if (!JS_GetElement(cx, jsarray, initedCount, ¤t) || \ |
|
1639 !JSData2Native(((_t*)array)+initedCount, current, type, \ |
|
1640 true, iid, pErr)) \ |
|
1641 goto failure; \ |
|
1642 } \ |
|
1643 PR_END_MACRO |
|
1644 |
|
1645 // No Action, FRee memory, RElease object |
|
1646 enum CleanupMode {na, fr, re}; |
|
1647 |
|
1648 CleanupMode cleanupMode; |
|
1649 |
|
1650 void *array = nullptr; |
|
1651 uint32_t initedCount; |
|
1652 RootedValue current(cx); |
|
1653 |
|
1654 // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*) |
|
1655 // XXX make extra space at end of char* and wchar* and null termintate |
|
1656 |
|
1657 switch (type.TagPart()) { |
|
1658 case nsXPTType::T_I8 : POPULATE(na, int8_t); break; |
|
1659 case nsXPTType::T_I16 : POPULATE(na, int16_t); break; |
|
1660 case nsXPTType::T_I32 : POPULATE(na, int32_t); break; |
|
1661 case nsXPTType::T_I64 : POPULATE(na, int64_t); break; |
|
1662 case nsXPTType::T_U8 : POPULATE(na, uint8_t); break; |
|
1663 case nsXPTType::T_U16 : POPULATE(na, uint16_t); break; |
|
1664 case nsXPTType::T_U32 : POPULATE(na, uint32_t); break; |
|
1665 case nsXPTType::T_U64 : POPULATE(na, uint64_t); break; |
|
1666 case nsXPTType::T_FLOAT : POPULATE(na, float); break; |
|
1667 case nsXPTType::T_DOUBLE : POPULATE(na, double); break; |
|
1668 case nsXPTType::T_BOOL : POPULATE(na, bool); break; |
|
1669 case nsXPTType::T_CHAR : POPULATE(na, char); break; |
|
1670 case nsXPTType::T_WCHAR : POPULATE(na, jschar); break; |
|
1671 case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure; |
|
1672 case nsXPTType::T_IID : POPULATE(fr, nsID*); break; |
|
1673 case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure; |
|
1674 case nsXPTType::T_CHAR_STR : POPULATE(fr, char*); break; |
|
1675 case nsXPTType::T_WCHAR_STR : POPULATE(fr, jschar*); break; |
|
1676 case nsXPTType::T_INTERFACE : POPULATE(re, nsISupports*); break; |
|
1677 case nsXPTType::T_INTERFACE_IS : POPULATE(re, nsISupports*); break; |
|
1678 case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); goto failure; |
|
1679 case nsXPTType::T_CSTRING : NS_ERROR("bad type"); goto failure; |
|
1680 case nsXPTType::T_ASTRING : NS_ERROR("bad type"); goto failure; |
|
1681 default : NS_ERROR("bad type"); goto failure; |
|
1682 } |
|
1683 |
|
1684 *d = array; |
|
1685 if (pErr) |
|
1686 *pErr = NS_OK; |
|
1687 return true; |
|
1688 |
|
1689 failure: |
|
1690 // we may need to cleanup the partially filled array of converted stuff |
|
1691 if (array) { |
|
1692 if (cleanupMode == re) { |
|
1693 nsISupports** a = (nsISupports**) array; |
|
1694 for (uint32_t i = 0; i < initedCount; i++) { |
|
1695 nsISupports* p = a[i]; |
|
1696 NS_IF_RELEASE(p); |
|
1697 } |
|
1698 } else if (cleanupMode == fr) { |
|
1699 void** a = (void**) array; |
|
1700 for (uint32_t i = 0; i < initedCount; i++) { |
|
1701 void* p = a[i]; |
|
1702 if (p) nsMemory::Free(p); |
|
1703 } |
|
1704 } |
|
1705 nsMemory::Free(array); |
|
1706 } |
|
1707 |
|
1708 return false; |
|
1709 |
|
1710 #undef POPULATE |
|
1711 } |
|
1712 |
|
1713 // static |
|
1714 bool |
|
1715 XPCConvert::NativeStringWithSize2JS(MutableHandleValue d, const void* s, |
|
1716 const nsXPTType& type, |
|
1717 uint32_t count, |
|
1718 nsresult* pErr) |
|
1719 { |
|
1720 NS_PRECONDITION(s, "bad param"); |
|
1721 |
|
1722 AutoJSContext cx; |
|
1723 if (pErr) |
|
1724 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; |
|
1725 |
|
1726 switch (type.TagPart()) { |
|
1727 case nsXPTType::T_PSTRING_SIZE_IS: |
|
1728 { |
|
1729 char* p = *((char**)s); |
|
1730 if (!p) |
|
1731 break; |
|
1732 JSString* str; |
|
1733 if (!(str = JS_NewStringCopyN(cx, p, count))) |
|
1734 return false; |
|
1735 d.setString(str); |
|
1736 break; |
|
1737 } |
|
1738 case nsXPTType::T_PWSTRING_SIZE_IS: |
|
1739 { |
|
1740 jschar* p = *((jschar**)s); |
|
1741 if (!p) |
|
1742 break; |
|
1743 JSString* str; |
|
1744 if (!(str = JS_NewUCStringCopyN(cx, p, count))) |
|
1745 return false; |
|
1746 d.setString(str); |
|
1747 break; |
|
1748 } |
|
1749 default: |
|
1750 XPC_LOG_ERROR(("XPCConvert::NativeStringWithSize2JS : unsupported type")); |
|
1751 return false; |
|
1752 } |
|
1753 return true; |
|
1754 } |
|
1755 |
|
1756 // static |
|
1757 bool |
|
1758 XPCConvert::JSStringWithSize2Native(void* d, HandleValue s, |
|
1759 uint32_t count, const nsXPTType& type, |
|
1760 nsresult* pErr) |
|
1761 { |
|
1762 NS_PRECONDITION(!JSVAL_IS_NULL(s), "bad param"); |
|
1763 NS_PRECONDITION(d, "bad param"); |
|
1764 |
|
1765 AutoJSContext cx; |
|
1766 uint32_t len; |
|
1767 |
|
1768 if (pErr) |
|
1769 *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; |
|
1770 |
|
1771 switch (type.TagPart()) { |
|
1772 case nsXPTType::T_PSTRING_SIZE_IS: |
|
1773 { |
|
1774 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) { |
|
1775 if (0 != count) { |
|
1776 if (pErr) |
|
1777 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING; |
|
1778 return false; |
|
1779 } |
|
1780 if (0 != count) { |
|
1781 len = (count + 1) * sizeof(char); |
|
1782 if (!(*((void**)d) = nsMemory::Alloc(len))) |
|
1783 return false; |
|
1784 return true; |
|
1785 } |
|
1786 // else ... |
|
1787 |
|
1788 *((char**)d) = nullptr; |
|
1789 return true; |
|
1790 } |
|
1791 |
|
1792 JSString* str = ToString(cx, s); |
|
1793 if (!str) { |
|
1794 return false; |
|
1795 } |
|
1796 |
|
1797 size_t length = JS_GetStringEncodingLength(cx, str); |
|
1798 if (length == size_t(-1)) { |
|
1799 return false; |
|
1800 } |
|
1801 if (length > count) { |
|
1802 if (pErr) |
|
1803 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING; |
|
1804 return false; |
|
1805 } |
|
1806 len = uint32_t(length); |
|
1807 |
|
1808 if (len < count) |
|
1809 len = count; |
|
1810 |
|
1811 uint32_t alloc_len = (len + 1) * sizeof(char); |
|
1812 char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len)); |
|
1813 if (!buffer) { |
|
1814 return false; |
|
1815 } |
|
1816 JS_EncodeStringToBuffer(cx, str, buffer, len); |
|
1817 buffer[len] = '\0'; |
|
1818 *((char**)d) = buffer; |
|
1819 |
|
1820 return true; |
|
1821 } |
|
1822 |
|
1823 case nsXPTType::T_PWSTRING_SIZE_IS: |
|
1824 { |
|
1825 const jschar* chars=nullptr; |
|
1826 JSString* str; |
|
1827 |
|
1828 if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) { |
|
1829 if (0 != count) { |
|
1830 if (pErr) |
|
1831 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING; |
|
1832 return false; |
|
1833 } |
|
1834 |
|
1835 if (0 != count) { |
|
1836 len = (count + 1) * sizeof(jschar); |
|
1837 if (!(*((void**)d) = nsMemory::Alloc(len))) |
|
1838 return false; |
|
1839 return true; |
|
1840 } |
|
1841 |
|
1842 // else ... |
|
1843 *((const jschar**)d) = nullptr; |
|
1844 return true; |
|
1845 } |
|
1846 |
|
1847 if (!(str = ToString(cx, s))) { |
|
1848 return false; |
|
1849 } |
|
1850 |
|
1851 len = JS_GetStringLength(str); |
|
1852 if (len > count) { |
|
1853 if (pErr) |
|
1854 *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING; |
|
1855 return false; |
|
1856 } |
|
1857 if (len < count) |
|
1858 len = count; |
|
1859 |
|
1860 if (!(chars = JS_GetStringCharsZ(cx, str))) { |
|
1861 return false; |
|
1862 } |
|
1863 uint32_t alloc_len = (len + 1) * sizeof(jschar); |
|
1864 if (!(*((void**)d) = nsMemory::Alloc(alloc_len))) { |
|
1865 // XXX should report error |
|
1866 return false; |
|
1867 } |
|
1868 memcpy(*((jschar**)d), chars, alloc_len); |
|
1869 (*((jschar**)d))[count] = 0; |
|
1870 |
|
1871 return true; |
|
1872 } |
|
1873 default: |
|
1874 XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type")); |
|
1875 return false; |
|
1876 } |
|
1877 } |
|
1878 |