2 // Tizen Web Device API
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
18 #include <SecurityExceptions.h>
21 #include <JSWebAPIException.h>
22 #include <ArgumentValidator.h>
23 #include <GlobalContextManager.h>
25 #include "plugin_config.h"
26 #include "JSBluetoothAdapter.h"
27 #include "BluetoothAdapter.h"
29 #include <TimeTracer.h>
32 using namespace WrtDeviceApis::Commons;
33 using namespace DeviceAPI::Common;
38 JSClassDefinition JSBluetoothAdapter::m_classInfo = {
40 kJSClassAttributeNone,
43 m_property, //StaticValues
44 m_function, //StaticFunctions
45 initialize, //Initialize
50 NULL, //DeleteProperty,
51 NULL, //GetPropertyNames,
52 NULL, //CallAsFunction,
53 NULL, //CallAsConstructor,
58 JSStaticValue JSBluetoothAdapter::m_property[] = {
59 { BLUETOOTH_ADAPTER_NAME, getProperty, NULL, kJSPropertyAttributeNone|kJSPropertyAttributeReadOnly|kJSPropertyAttributeDontDelete },
60 { BLUETOOTH_ADAPTER_POWERED, getProperty, NULL, kJSPropertyAttributeNone|kJSPropertyAttributeReadOnly|kJSPropertyAttributeDontDelete },
61 { BLUETOOTH_ADAPTER_VISIBLE, getProperty, NULL, kJSPropertyAttributeNone|kJSPropertyAttributeReadOnly|kJSPropertyAttributeDontDelete },
62 { BLUETOOTH_ADAPTER_ADDRESS, getProperty, NULL, kJSPropertyAttributeNone|kJSPropertyAttributeReadOnly|kJSPropertyAttributeDontDelete },
66 JSStaticFunction JSBluetoothAdapter::m_function[] = {
67 { BLUETOOTH_ADAPTER_API_SET_NAME, setName, kJSPropertyAttributeNone },
68 { BLUETOOTH_ADAPTER_API_SET_POWERED, setPowered, kJSPropertyAttributeNone },
69 { BLUETOOTH_ADAPTER_API_SET_VISIBLE, setVisible, kJSPropertyAttributeNone },
70 { BLUETOOTH_ADAPTER_API_DISCOVER_DEVICES, discoverDevices, kJSPropertyAttributeNone },
71 { BLUETOOTH_ADAPTER_API_STOP_DISCOVERY, stopDiscovery, kJSPropertyAttributeNone },
72 { BLUETOOTH_ADAPTER_API_GET_KNOWN_DEVICES, getKnownDevices, kJSPropertyAttributeNone },
73 { BLUETOOTH_ADAPTER_API_GET_DEVICE, getDevice, kJSPropertyAttributeNone },
74 { BLUETOOTH_ADAPTER_API_CREATE_BONDING, createBonding, kJSPropertyAttributeNone },
75 { BLUETOOTH_ADAPTER_API_DESTROY_BONDING, destroyBonding, kJSPropertyAttributeNone },
76 { BLUETOOTH_ADAPTER_API_REGISTER_RFCOMMSERVICE_BY_UUID, registerRFCOMMServiceByUUID, kJSPropertyAttributeNone },
80 JSClassRef JSBluetoothAdapter::m_jsClassRef = JSClassCreate(JSBluetoothAdapter::getClassInfo());
82 const JSClassRef JSBluetoothAdapter::getClassRef()
85 m_jsClassRef = JSClassCreate(&m_classInfo);
90 const JSClassDefinition* JSBluetoothAdapter::getClassInfo()
95 JSObjectRef JSBluetoothAdapter::createJSObject(JSContextRef context)
97 return JSObjectMake(context, getClassRef(), NULL);
100 void JSBluetoothAdapter::initialize(JSContextRef context, JSObjectRef object)
107 void JSBluetoothAdapter::finalize(JSObjectRef object)
113 JSValueRef JSBluetoothAdapter::getProperty(JSContextRef context,
115 JSStringRef propertyName,
116 JSValueRef* exception)
120 if (JSStringIsEqualToUTF8CString(propertyName, BLUETOOTH_ADAPTER_NAME)) {
121 std::string name = BluetoothAdapter::getInstance()->getName();
122 LoggerD("name: " << name);
123 return JSUtil::toJSValueRef(context, name);
125 else if (JSStringIsEqualToUTF8CString(propertyName, BLUETOOTH_ADAPTER_POWERED)) {
126 return JSUtil::toJSValueRef(context, BluetoothAdapter::getInstance()->getPowered());
128 else if (JSStringIsEqualToUTF8CString(propertyName, BLUETOOTH_ADAPTER_VISIBLE)) {
129 return JSUtil::toJSValueRef(context, BluetoothAdapter::getInstance()->getVisible());
131 else if (JSStringIsEqualToUTF8CString(propertyName, BLUETOOTH_DEVICE_ADDRESS)) {
132 std::string address = BluetoothAdapter::getInstance()->getAddress();
133 LoggerD("address: " << address);
134 return JSUtil::toJSValueRef(context, address);
136 } catch (const BasePlatformException &err) {
137 LoggerW("Getting property is failed" << err.getMessage().c_str());
143 JSValueRef JSBluetoothAdapter::setName(JSContextRef context,
145 JSObjectRef thisObject,
146 size_t argumentCount,
147 const JSValueRef arguments[],
148 JSValueRef* exception)
151 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
154 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_SET_NAME);
155 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
158 // Check whether Bluetooth is supported or not
159 if(!BluetoothAdapter::isBluetoothSupported()) {
160 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
163 ArgumentValidator validator(context, argumentCount, arguments);
164 std::string name = validator.toString(0); // name
165 JSObjectRef successCallback = validator.toFunction(1, true); // successCallback
166 JSObjectRef errorCallback = validator.toFunction(2, true); // errorCallback
169 MultiCallbackUserDataPtr callback(
170 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
172 LoggerW("Can't create MultiCallbackUserData");
175 callback->setCallback("success", successCallback);
176 callback->setCallback("error", errorCallback);
179 BluetoothAdapter::getInstance()->setName(name, callback);
180 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
182 return JSValueMakeUndefined(context);
183 } catch (const BasePlatformException &err) {
184 return JSWebAPIException::throwException(context, exception, err);
186 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.setName().");
187 return JSWebAPIException::throwException(context, exception, err);
191 JSValueRef JSBluetoothAdapter::setPowered(JSContextRef context,
193 JSObjectRef thisObject,
194 size_t argumentCount,
195 const JSValueRef arguments[],
196 JSValueRef* exception)
199 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
202 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_SET_POWERED);
203 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
206 // Check whether Bluetooth is supported or not
207 if(!BluetoothAdapter::isBluetoothSupported()) {
208 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
211 // Validate arguments
212 ArgumentValidator validator(context, argumentCount, arguments);
213 bool state = validator.toBool(0); // state
214 JSObjectRef successCallback = validator.toFunction(1, true); // successCallback
215 JSObjectRef errorCallback = validator.toFunction(2, true); // errorCallback
218 MultiCallbackUserDataPtr callback(
219 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
221 LoggerW("Can't create MultiCallbackUserData");
224 callback->setCallback("success", successCallback);
225 callback->setCallback("error", errorCallback);
228 BluetoothAdapter::getInstance()->setPowered(state, callback);
229 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
231 return JSValueMakeUndefined(context);
232 } catch (const BasePlatformException &err) {
233 return JSWebAPIException::throwException(context, exception, err);
235 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.setPowered().");
236 return JSWebAPIException::throwException(context, exception, err);
240 JSValueRef JSBluetoothAdapter::setVisible(JSContextRef context,
242 JSObjectRef thisObject,
243 size_t argumentCount,
244 const JSValueRef arguments[],
245 JSValueRef* exception)
248 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
251 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_SET_VISIBLE);
252 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
255 // Check whether Bluetooth is supported or not
256 if(!BluetoothAdapter::isBluetoothSupported()) {
257 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
260 // Validate arguments
261 ArgumentValidator validator(context, argumentCount, arguments);
262 bool mode = validator.toBool(0); // mode
263 JSObjectRef successCallback = validator.toFunction(1, true); // successCallback
264 JSObjectRef errorCallback = validator.toFunction(2, true); // errorCallback
265 unsigned long timeout = validator.toULong(3, true); // timeout
270 MultiCallbackUserDataPtr callback(
271 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
273 LoggerW("Can't create MultiCallbackUserData");
276 callback->setCallback("success", successCallback);
277 callback->setCallback("error", errorCallback);
280 BluetoothAdapter::getInstance()->setVisible(mode, timeout, callback);
281 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
283 return JSValueMakeUndefined(context);
284 } catch (const BasePlatformException &err) {
285 return JSWebAPIException::throwException(context, exception, err);
287 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.setVisible().");
288 return JSWebAPIException::throwException(context, exception, err);
292 JSValueRef JSBluetoothAdapter::discoverDevices(JSContextRef context,
294 JSObjectRef thisObject,
295 size_t argumentCount,
296 const JSValueRef arguments[],
297 JSValueRef* exception)
300 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
303 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_DISCOVER_DEVICES);
304 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
307 // Check whether Bluetooth is supported or not
308 if(!BluetoothAdapter::isBluetoothSupported()) {
309 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
312 // Validate arguments
313 ArgumentValidator validator(context, argumentCount, arguments);
316 JSObjectRef successCallback = validator.toCallbackObject(0, false, "onstarted", "ondevicefound", "ondevicedisappeared", "onfinished", NULL);
319 JSObjectRef errorCallback = validator.toFunction(1, true);
321 MultiCallbackUserDataPtr callback(
322 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
324 LoggerW("Can't create MultiMultiCallbackUserData");
328 JSValueRef onstartedValue = JSUtil::getProperty(context , successCallback, "onstarted");
329 if(!JSValueIsUndefined(context, onstartedValue)) {
330 LoggerD("There is a onstarted()");
331 callback->setCallback("onstarted", JSUtil::JSValueToObject(context, onstartedValue));
335 JSValueRef ondevicefoundValue = JSUtil::getProperty(context , successCallback, "ondevicefound");
336 if(!JSValueIsUndefined(context, ondevicefoundValue)) {
337 LoggerD("There is a ondevicefound()");
338 callback->setCallback("ondevicefound", JSUtil::JSValueToObject(context, ondevicefoundValue));
341 // ondevicedisappeared
342 JSValueRef ondevicedisappearedValue = JSUtil::getProperty(context , successCallback, "ondevicedisappeared");
343 if(!JSValueIsUndefined(context, ondevicedisappearedValue)) {
344 LoggerD("There is a ondevicedisappeared()");
345 callback->setCallback("ondevicedisappeared", JSUtil::JSValueToObject(context, ondevicedisappearedValue));
349 JSValueRef onfinishedValue = JSUtil::getProperty(context , successCallback, "onfinished");
350 if(!JSValueIsUndefined(context, onfinishedValue)) {
351 LoggerD("There is a onfinished()");
352 callback->setCallback("onfinished", JSUtil::JSValueToObject(context, onfinishedValue));
355 callback->setCallback("error", errorCallback);
359 BluetoothAdapter::getInstance()->discoverDevices(callback);
360 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
362 return JSValueMakeUndefined(context);
363 } catch (const BasePlatformException &err) {
364 return JSWebAPIException::throwException(context, exception, err);
366 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.discoverDevices().");
367 return JSWebAPIException::throwException(context, exception, err);
371 JSValueRef JSBluetoothAdapter::stopDiscovery(JSContextRef context,
373 JSObjectRef thisObject,
374 size_t argumentCount,
375 const JSValueRef arguments[],
376 JSValueRef* exception)
379 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
382 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_STOP_DISCOVERY);
383 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
386 // Check whether Bluetooth is supported or not
387 if(!BluetoothAdapter::isBluetoothSupported()) {
388 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
391 // Validate arguments
392 ArgumentValidator validator(context, argumentCount, arguments);
393 JSObjectRef successCallback = validator.toFunction(0, true); // successCallback
394 JSObjectRef errorCallback = validator.toFunction(1, true); // errorCallback
397 MultiCallbackUserDataPtr callback(
398 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
400 LoggerW("Can't create MultiCallbackUserData");
403 callback->setCallback("success", successCallback);
404 callback->setCallback("error", errorCallback);
407 BluetoothAdapter::getInstance()->stopDiscovery(callback);
408 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
410 return JSValueMakeUndefined(context);
411 } catch (const BasePlatformException &err) {
412 return JSWebAPIException::throwException(context, exception, err);
414 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.stopDiscovery().");
415 return JSWebAPIException::throwException(context, exception, err);
419 JSValueRef JSBluetoothAdapter::getKnownDevices(JSContextRef context,
421 JSObjectRef thisObject,
422 size_t argumentCount,
423 const JSValueRef arguments[],
424 JSValueRef* exception)
427 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
430 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_GET_KNOWN_DEVICES);
431 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
434 // Check whether Bluetooth is supported or not
435 if(!BluetoothAdapter::isBluetoothSupported()) {
436 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
439 // Validate arguments
440 ArgumentValidator validator(context, argumentCount, arguments);
441 JSObjectRef successCallback = validator.toFunction(0); // successCallback
442 JSObjectRef errorCallback = validator.toFunction(1, true); // errorCallback
445 MultiCallbackUserDataPtr callback(
446 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
448 LoggerW("Can't create MultiCallbackUserData");
451 callback->setCallback("success", successCallback);
452 callback->setCallback("error", errorCallback);
455 BluetoothAdapter::getInstance()->getKnownDevices(callback);
456 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
458 return JSValueMakeUndefined(context);
459 } catch (const BasePlatformException &err) {
460 return JSWebAPIException::throwException(context, exception, err);
462 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.getKnownDevices().");
463 return JSWebAPIException::throwException(context, exception, err);
467 JSValueRef JSBluetoothAdapter::getDevice(JSContextRef context,
469 JSObjectRef thisObject,
470 size_t argumentCount,
471 const JSValueRef arguments[],
472 JSValueRef* exception)
475 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
478 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_GET_DEVICE);
479 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
482 // Check whether Bluetooth is supported or not
483 if(!BluetoothAdapter::isBluetoothSupported()) {
484 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
487 // Validate arguments
488 ArgumentValidator validator(context, argumentCount, arguments);
489 std::string address = validator.toString(0); // address
490 JSObjectRef successCallback = validator.toFunction(1); // successCallback
491 JSObjectRef errorCallback = validator.toFunction(2, true); // errorCallback
494 MultiCallbackUserDataPtr callback(
495 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
497 LoggerW("Can't create MultiCallbackUserData");
500 callback->setCallback("success", successCallback);
501 callback->setCallback("error", errorCallback);
504 BluetoothAdapter::getInstance()->getDevice(address, callback);
505 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
507 return JSValueMakeUndefined(context);
508 } catch (const BasePlatformException &err) {
509 return JSWebAPIException::throwException(context, exception, err);
511 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.getDevice().");
512 return JSWebAPIException::throwException(context, exception, err);
516 JSValueRef JSBluetoothAdapter::createBonding(JSContextRef context,
518 JSObjectRef thisObject,
519 size_t argumentCount,
520 const JSValueRef arguments[],
521 JSValueRef* exception)
524 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
527 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_CREATE_BONDING);
528 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
531 // Check whether Bluetooth is supported or not
532 if(!BluetoothAdapter::isBluetoothSupported()) {
533 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
536 // Validate arguments
537 ArgumentValidator validator(context, argumentCount, arguments);
538 std::string address = validator.toString(0); // address
539 JSObjectRef successCallback = validator.toFunction(1); // successCallback
540 JSObjectRef errorCallback = validator.toFunction(2, true); // errorCallback
543 MultiCallbackUserDataPtr callback(
544 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
546 LoggerW("Can't create MultiCallbackUserData");
549 callback->setCallback("success", successCallback);
550 callback->setCallback("error", errorCallback);
553 BluetoothAdapter::getInstance()->createBonding(address, callback);
554 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
556 return JSValueMakeUndefined(context);
557 } catch (const BasePlatformException &err) {
558 return JSWebAPIException::throwException(context, exception, err);
560 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.createBonding().");
561 return JSWebAPIException::throwException(context, exception, err);
565 JSValueRef JSBluetoothAdapter::destroyBonding(JSContextRef context,
567 JSObjectRef thisObject,
568 size_t argumentCount,
569 const JSValueRef arguments[],
570 JSValueRef* exception)
573 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
576 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_DESTROY_BONDING);
577 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
580 // Check whether Bluetooth is supported or not
581 if(!BluetoothAdapter::isBluetoothSupported()) {
582 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
585 // Validate arguments
586 ArgumentValidator validator(context, argumentCount, arguments);
587 std::string address = validator.toString(0); // address
588 JSObjectRef successCallback = validator.toFunction(1, true); // successCallback
589 JSObjectRef errorCallback = validator.toFunction(2, true); // errorCallback
592 MultiCallbackUserDataPtr callback(
593 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
595 LoggerW("Can't create MultiCallbackUserData");
598 callback->setCallback("success", successCallback);
599 callback->setCallback("error", errorCallback);
602 BluetoothAdapter::getInstance()->destroyBonding(address, callback);
603 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
605 return JSValueMakeUndefined(context);
606 } catch (const BasePlatformException &err) {
607 return JSWebAPIException::throwException(context, exception, err);
609 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.destroyBonding().");
610 return JSWebAPIException::throwException(context, exception, err);
614 JSValueRef JSBluetoothAdapter::registerRFCOMMServiceByUUID(JSContextRef context,
616 JSObjectRef thisObject,
617 size_t argumentCount,
618 const JSValueRef arguments[],
619 JSValueRef* exception)
622 TIME_TRACER_ITEM_BEGIN(__FUNCTION__, 0);
625 AceSecurityStatus status = BLUETOOTH_CHECK_ACCESS(BLUETOOTH_ADAPTER_API_REGISTER_RFCOMMSERVICE_BY_UUID);
626 TIZEN_SYNC_ACCESS_HANDLER(status, context, exception);
629 // Check whether Bluetooth is supported or not
630 if(!BluetoothAdapter::isBluetoothSupported()) {
631 throw DeviceAPI::Common::NotSupportedException("Bluetooth is not supported");
634 // Validate arguments
635 ArgumentValidator validator(context, argumentCount, arguments);
636 std::string uuid = validator.toString(0); // uuid
637 std::string name = validator.toString(1); // name
638 JSObjectRef successCallback = validator.toFunction(2); // successCallback
639 JSObjectRef errorCallback = validator.toFunction(3, true); // errorCallback
642 MultiCallbackUserDataPtr callback(
643 new MultiCallbackUserData(GlobalContextManager::getInstance()->getGlobalContext(context)));
645 LoggerW("Can't create MultiCallbackUserData");
648 callback->setCallback("success", successCallback);
649 callback->setCallback("error", errorCallback);
652 BluetoothAdapter::getInstance()->registerRFCOMMServiceByUUID(uuid, name, callback);
653 TIME_TRACER_ITEM_END(__FUNCTION__, 0);
655 return JSValueMakeUndefined(context);
656 } catch (const BasePlatformException &err) {
657 return JSWebAPIException::throwException(context, exception, err);
659 DeviceAPI::Common::UnknownException err("Unknown Error in BluetoothAdapter.registerRFCOMMServiceByUUID().");
660 return JSWebAPIException::throwException(context, exception, err);