Add 'self' as global scope for web worker
[platform/core/api/webapi-plugins.git] / src / utils / utils_api.js
1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 //Object xwalk.JSON - guaranteed to not being modified by the application programmer
7 var JSON_ = { stringify: JSON.stringify, parse: JSON.parse };
8 Object.freeze(JSON_);
9 exports.JSON = JSON_;
10
11 var _enableJsLogs = false;
12
13 var _global = {};
14 if (typeof window !== 'undefined') {
15     _global = window;
16 } else if (typeof global !== 'undefined') {
17     _global = global;
18 } else if (typeof self !== 'undefined') {
19     _global = self;
20 }
21
22 /**
23  * @brief CommonListenerManager constructor function.
24  * @param nativeMgr NativeManager handle.
25  * @param managerName Name which will be used as native listener name.
26  * @param onIdNotFound Callback without arguments invoked when trying to remove not
27  *                      existing listener. If unspecified it is replaced by empty
28  *                      function.
29  */
30 var CommonListenerManager = function(nativeMgr, managerName, onIdNotFound) {
31     this.listeners = {};
32     this.nextId = 1;
33     this.numberOfListeners = 0;
34     this.name = managerName;
35     this.native = nativeMgr;
36     this.hasNativeListener = false;
37     if (onIdNotFound === undefined) {
38         this.idNotFoundBehavior = function() {};
39     } else {
40         this.idNotFoundBehavior = onIdNotFound;
41     }
42 };
43
44 /**
45  * @brief Callback for native listener, which invokes all existing listeners.
46  * @param msg native Arguments object for native listener callback.
47  */
48 CommonListenerManager.prototype.onListenerCalled = function(msg) {
49     for (var watchId in this.listeners) {
50         if (this.listeners.hasOwnProperty(watchId)) {
51             this.listeners[watchId](msg, watchId);
52         }
53     }
54 };
55
56 /**
57  * @brief Registers new listener.
58  * @param callback Callback function taking single argument.
59  * @retval integer listener id. Use it later to remove listener.
60  */
61 CommonListenerManager.prototype.addListener = function(callback) {
62     if (!this.hasNativeListener) {
63         this.native.addListener(this.name, this.onListenerCalled.bind(this));
64         this.hasNativeListener = true;
65     }
66     var id = this.nextId++;
67     this.listeners[id] = callback;
68     this.numberOfListeners++;
69     return id;
70 };
71
72 /**
73  * @brief Remove previously registered listener.
74  *
75  * If listener for given id is not found, onIdNotFound callback is invoked.
76  *
77  * @param watchId Listener id returned by addListener.
78  * @retval onIdNotFoundCallback return value.
79  */
80 CommonListenerManager.prototype.removeListener = function(watchId) {
81     if (this.listeners.hasOwnProperty(watchId)) {
82         delete this.listeners[watchId];
83         this.numberOfListeners--;
84         if (this.numberOfListeners == 0) {
85             this.native.removeListener(this.name);
86             this.hasNativeListener = false;
87             this.nextId = 1;
88         }
89     } else {
90         return this.idNotFoundBehavior();
91     }
92 };
93
94 var DateConverter = function() {};
95
96 DateConverter.prototype.toTZDate = function(v, isAllDay) {
97     if (typeof v === 'number') {
98         v = {
99             UTCTimestamp: v
100         };
101         isAllDay = false;
102     }
103
104     if (!(v instanceof _global.Object)) {
105         return v;
106     }
107
108     if (isAllDay) {
109         return new tizen.TZDate(
110             v.year,
111             v.month - 1,
112             v.day,
113             null,
114             null,
115             null,
116             null,
117             v.timezone || null
118         );
119     } else {
120         return new tizen.TZDate(new Date(v.UTCTimestamp * 1000));
121     }
122 };
123
124 DateConverter.prototype.fromTZDate = function(v) {
125     if (!tizen.TZDate || !(v instanceof tizen.TZDate)) {
126         return v;
127     }
128
129     return {
130         year: v.getFullYear(),
131         month: v.getMonth(),
132         day: v.getDate(),
133         timezone: v.getTimezone(),
134         UTCTimestamp: v._utcTimestamp / 1000
135     };
136 };
137
138 var _dateConverter = new DateConverter();
139
140 /**
141  * Cynara(since tizen 3.0) only support native privilege.
142  * simply web privilege convert native privilege for checking access.
143  */
144 var _privilege = {
145     ACCOUNT_READ: 'http://tizen.org/privilege/account.read',
146     ACCOUNT_WRITE: 'http://tizen.org/privilege/account.write',
147     ALARM: 'http://tizen.org/privilege/alarm.get',
148     APPLICATION_INFO: 'http://tizen.org/privilege/application.info',
149     APPLICATION_LAUNCH: 'http://tizen.org/privilege/application.launch',
150     APPMANAGER_CERTIFICATE: 'http://tizen.org/privilege/appmanager.certificate',
151     APPMANAGER_KILL: 'http://tizen.org/privilege/appmanager.kill',
152     BLUETOOTH_ADMIN: 'http://tizen.org/privilege/bluetooth.admin',
153     BLUETOOTH_GAP: 'http://tizen.org/privilege/bluetooth.gap',
154     BLUETOOTH_HEALTH: 'http://tizen.org/privilege/bluetooth.health',
155     BLUETOOTH_SPP: 'http://tizen.org/privilege/bluetooth.spp',
156     BLUETOOTHMANAGER: 'http://tizen.org/privilege/bluetoothmanager',
157     BLUETOOTH: 'http://tizen.org/privilege/bluetooth',
158     CALENDAR_READ: 'http://tizen.org/privilege/calendar.read',
159     CALENDAR_WRITE: 'http://tizen.org/privilege/calendar.write',
160     CALLHISTORY_READ: 'http://tizen.org/privilege/callhistory.read',
161     CALLHISTORY_WRITE: 'http://tizen.org/privilege/callhistory.write',
162     CONTACT_READ: 'http://tizen.org/privilege/contact.read',
163     CONTACT_WRITE: 'http://tizen.org/privilege/contact.write',
164     CONTENT_READ: 'http://tizen.org/privilege/content.read',
165     CONTENT_WRITE: 'http://tizen.org/privilege/content.write',
166     DATACONTROL_CONSUMER: 'http://tizen.org/privilege/datacontrol.consumer',
167     DATASYNC: 'http://tizen.org/privilege/datasync',
168     DOWNLOAD: 'http://tizen.org/privilege/download',
169     FILESYSTEM_READ: 'http://tizen.org/privilege/filesystem.read',
170     FILESYSTEM_WRITE: 'http://tizen.org/privilege/filesystem.write',
171     HAPTIC: 'http://tizen.org/privilege/haptic',
172     HEALTHINFO: 'http://tizen.org/privilege/healthinfo',
173     INTERNET: 'http://tizen.org/privilege/internet',
174     LED: 'http://tizen.org/privilege/led',
175     LOCATION: 'http://tizen.org/privilege/location',
176     MEDIACONTROLLER_SERVER: 'http://tizen.org/privilege/mediacontroller.server',
177     MEDIACONTROLLER_CLIENT: 'http://tizen.org/privilege/mediacontroller.client',
178     MESSAGING_READ: 'http://tizen.org/privilege/messaging.read',
179     MESSAGING_WRITE: 'http://tizen.org/privilege/messaging.write',
180     NETWORKBEARERSELECTION: 'http://tizen.org/privilege/networkbearerselection',
181     NFC_ADMIN: 'http://tizen.org/privilege/nfc.admin',
182     NFC_CARDEMULATION: 'http://tizen.org/privilege/nfc.cardemulation',
183     NFC_COMMON: 'http://tizen.org/privilege/nfc.common',
184     NFC_P2P: 'http://tizen.org/privilege/nfc.p2p',
185     NFC_TAG: 'http://tizen.org/privilege/nfc.tag',
186     NOTIFICATION: 'http://tizen.org/privilege/notification',
187     PACKAGE_INFO: 'http://tizen.org/privilege/packagemanager.info',
188     PACKAGEMANAGER_INSTALL: 'http://tizen.org/privilege/packagemanager.install',
189     POWER: 'http://tizen.org/privilege/power',
190     PUSH: 'http://tizen.org/privilege/push',
191     SECUREELEMENT: 'http://tizen.org/privilege/secureelement',
192     SETTING_ADMIN: 'http://tizen.org/privilege/systemsettings.admin',
193     SETTING: 'http://tizen.org/privilege/setting',
194     SYSTEM: 'http://tizen.org/privilege/system',
195     SYSTEMMANAGER: 'http://tizen.org/privilege/systemmanager',
196     TELEPHONY: 'http://tizen.org/privilege/telephony',
197     VOLUME_SET: 'http://tizen.org/privilege/volume.set',
198     WEBSETTING: 'http://tizen.org/privilege/websetting',
199     TV_INPUT_DEVICE: 'http://tizen.org/privilege/tv.inputdevice'
200 };
201
202 Object.freeze(_privilege);
203
204 /** @constructor */
205 function Utils() {
206     Object.defineProperty(this, 'privilege', {
207         value: _privilege,
208         writable: false,
209         enumerable: true,
210         configurable: false
211     });
212 }
213
214 Utils.prototype.error = console.error.bind(console);
215 Utils.prototype.warn = console.warn.bind(console);
216 Utils.prototype.log = _enableJsLogs ? console.log.bind(console) : function() {};
217 var appVersion = undefined; // Used to cache required version of an app
218
219 /**
220  * @param {string} msg Message to be logged on warn level.
221  * @param {string} deprecationVersion Version from which the deprecation log must appear.
222  */
223 Utils.prototype.deprecationWarn = function(msg, deprecationVersion) {
224     // For public code, warning should be always shown
225     this.warn('DEPRECATION WARNING: ' + msg);
226 };
227
228 if (console.assert) {
229     Utils.prototype.assert = console.assert.bind(console);
230 } else {
231     Utils.prototype.assert = function() {
232         if (false === arguments[0]) {
233             console.error('Assertion failed: ', Array.prototype.slice.call(arguments, 1));
234         }
235     };
236 }
237
238 Utils.prototype.global = _global;
239
240 Utils.prototype.repackFilter = function(filter) {
241     if (filter instanceof tizen.AttributeFilter) {
242         return {
243             filterType: 'AttributeFilter',
244             attributeName: filter.attributeName,
245             matchFlag: filter.matchFlag,
246             matchValue: _dateConverter.fromTZDate(filter.matchValue)
247         };
248     }
249     if (filter instanceof tizen.AttributeRangeFilter) {
250         return {
251             filterType: 'AttributeRangeFilter',
252             attributeName: filter.attributeName,
253             initialValue: _dateConverter.fromTZDate(filter.initialValue),
254             endValue: _dateConverter.fromTZDate(filter.endValue)
255         };
256     }
257     if (filter instanceof tizen.CompositeFilter) {
258         var _f = [];
259         var filters = filter.filters;
260
261         for (var i = 0; i < filters.length; ++i) {
262             _f.push(this.repackFilter(filters[i]));
263         }
264
265         return {
266             filterType: 'CompositeFilter',
267             type: filter.type,
268             filters: _f
269         };
270     }
271
272     return null;
273 };
274
275 var apiVersion = null;
276 Utils.prototype.getPkgApiVersion = function() {
277     if (apiVersion) {
278         return apiVersion;
279     }
280     var result = native_.callSync('UtilsGetPkgApiVersion');
281     if (native_.isFailure(result)) {
282         throw native_.getErrorObject(result);
283     }
284     apiVersion = native_.getResultObject(result);
285     return apiVersion;
286 };
287
288 var isPrivilege = function(toCheck) {
289     if (Object.values(_privilege).indexOf(toCheck) < 0) {
290         return false;
291     }
292     return true;
293 };
294
295 var cachedPrivileges = {};
296 Utils.prototype.checkPrivilegeAccess = function(privilege) {
297     if (!isPrivilege(privilege)) {
298         xwalk.utils.error(
299             'Privilege ' + privilege + ' does not exist. Please fix your code.'
300         );
301         throw new WebAPIException(WebAPIException.SECURITY_ERR);
302     }
303
304     if (cachedPrivileges[privilege]) {
305         return;
306     }
307     var result = native_.callSync('UtilsCheckPrivilegeAccess', {
308         privilege: _toString(privilege)
309     });
310     var isFailure = native_.isFailure(result);
311     cachedPrivileges[privilege] = !isFailure;
312     if (isFailure) {
313         throw native_.getErrorObject(result);
314     }
315 };
316
317 Utils.prototype.isAppVersionEarlierThan = function(ver) {
318     var app_ver = this.getPkgApiVersion();
319
320     var arr_ver = ver.split('.'); // reference version
321     var arr_app_ver = app_ver.split('.'); // application version
322     var num_ver;
323     var num_app;
324
325     var i;
326     var length = Math.min(arr_ver.length, arr_app_ver.length);
327     for (i = 0; i < length; i++) {
328         num_ver = parseInt(arr_ver[i]);
329         num_app = parseInt(arr_app_ver[i]);
330         if (num_app < num_ver) {
331             return true;
332         } else if (num_app > num_ver) {
333             return false;
334         }
335     }
336
337     if (arr_ver.length > arr_app_ver.length) {
338         return true;
339     }
340     return false;
341 };
342
343 Utils.prototype.checkPrivilegeAccess4Ver = function(new_ver, new_priv, old_priv) {
344     if (!this.isAppVersionEarlierThan(new_ver)) {
345         this.checkPrivilegeAccess(new_priv);
346     } else if (old_priv != undefined) {
347         this.checkPrivilegeAccess(old_priv);
348     }
349 };
350
351 Utils.prototype.checkBackwardCompabilityPrivilegeAccess = function(
352     current_privilege,
353     previous_privilege
354 ) {
355     var result = native_.callSync('UtilsCheckBackwardCompabilityPrivilegeAccess', {
356         current_privilege: _toString(current_privilege),
357         previous_privilege: _toString(previous_privilege)
358     });
359
360     if (native_.isFailure(result)) {
361         throw native_.getErrorObject(result);
362     }
363 };
364
365 Utils.prototype.checkProfile = function() {
366     var result = native_.callSync('UtilsCheckProfile', {});
367
368     return native_.getResultObject(result);
369 };
370
371 Utils.prototype.printDeprecationWarningFor = function(name, replacement) {
372     if (_type.isUndefined(replacement)) {
373         this.warn(
374             'DEPRECATION WARNING: ' +
375                 name +
376                 ' is deprecated and using it is not recommended.'
377         );
378     } else {
379         this.warn(
380             'DEPRECATION WARNING: ' +
381                 name +
382                 ' is deprecated and using it is not recommended.' +
383                 'Try using ' +
384                 replacement +
385                 ' instead.'
386         );
387     }
388 };
389
390 /*
391  * Pass array-like object of numbers (Array, Uint8Array, etc.), returns string.
392  * Each char has codepoint equal to value from array cropped with & 0xFF
393  * Useful for passing data through crosswalk.
394  */
395 Utils.prototype.ArrayToString = function(data) {
396     var output = '';
397     var len = data.length;
398     for (var i = 0; i < len; i++) {
399         output += String.fromCharCode(data[i] & 0xff); // conversion to octet
400     }
401     return output;
402 };
403
404 /*
405  * Create new array-like object of numbers: UTF-16 char codes from string.
406  * As type pass Array, Uint8Array, etc.
407  * Useful for passing data through crosswalk.
408  */
409 Utils.prototype.StringToArray = function(str, type) {
410     var len = str.length;
411     var output = new type(len);
412     for (var i = 0; i < len; i++) {
413         output[i] = str.charCodeAt(i);
414     }
415     return output;
416 };
417
418 /////////////////////////////////////////////////////////////////////////////
419 /** @constructor */
420 var Type = function() {};
421
422 Type.prototype.isBoolean = function(obj) {
423     return typeof obj === 'boolean';
424 };
425
426 Type.prototype.isObject = function(obj) {
427     return null !== obj && typeof obj === 'object' && !this.isArray(obj);
428 };
429
430 Type.prototype.isArray = function(obj) {
431     return Array.isArray(obj);
432 };
433
434 Type.prototype.isOctet = function(value) {
435     return Number.isInteger(value) && 0 <= value && value <= 255;
436 };
437
438 Type.prototype.isByteStream = function(value) {
439     return value instanceof Uint8Array;
440 };
441
442 Type.prototype.isByteStreamArray = function(value) {
443     return Array.isArray(value) && value.every(this.isByteStream);
444 };
445
446 Type.prototype.isLegacyByteStream = function(value) {
447     return Array.isArray(value) && value.every(this.isOctet);
448 };
449
450 Type.prototype.isLegacyByteStreamArray = function(value) {
451     return (
452         Array.isArray(value) &&
453         value.every(
454             function(x) {
455                 return this.isLegacyByteStream(x);
456             }.bind(this)
457         )
458     );
459 };
460
461 Type.prototype.isFunction = function(obj) {
462     return typeof obj === 'function';
463 };
464
465 Type.prototype.isNumber = function(obj) {
466     return typeof obj === 'number';
467 };
468
469 Type.prototype.isString = function(obj) {
470     return typeof obj === 'string';
471 };
472
473 Type.prototype.isStringArray = function(value) {
474     return Array.isArray(value) && value.every(this.isString);
475 };
476
477 Type.prototype.isDate = function(obj) {
478     return obj instanceof Date;
479 };
480
481 Type.prototype.isNull = function(obj) {
482     return obj === null;
483 };
484
485 Type.prototype.isNullOrUndefined = function(obj) {
486     return obj === null || obj === undefined;
487 };
488
489 Type.prototype.isUndefined = function(obj) {
490     return obj === void 0;
491 };
492
493 Type.prototype.isA = function(obj, type) {
494     var clas = Object.prototype.toString.call(obj).slice(8, -1);
495     return obj !== undefined && obj !== null && clas === type;
496 };
497
498 Type.prototype.isEmptyObject = function(obj) {
499     for (var property in obj) {
500         if (obj.hasOwnProperty(property)) {
501             return false;
502         }
503     }
504     return true;
505 };
506
507 Type.prototype.hasProperty = function(obj, prop) {
508     return prop in obj;
509 };
510
511 Type.prototype.arrayContains = function(arr, value) {
512     return arr.indexOf(value) > -1;
513 };
514
515 Type.prototype.getValues = function(obj) {
516     var ret = [];
517     for (var key in obj) {
518         if (obj.hasOwnProperty(key)) {
519             ret.push(obj[key]);
520         }
521     }
522     return ret;
523 };
524
525 var _type = new Type();
526
527 /////////////////////////////////////////////////////////////////////////////
528 /** @constructor */
529 var Converter = function() {};
530
531 function _nullableGeneric(func, nullable, val) {
532     if (_type.isNull(val) && nullable === true) {
533         return val;
534     } else {
535         return func.apply(null, [].slice.call(arguments, 2));
536     }
537 }
538
539 function _toBoolean(val) {
540     return Boolean(val);
541 }
542
543 Converter.prototype.toBoolean = function(val, nullable) {
544     return _nullableGeneric(_toBoolean, nullable, val);
545 };
546
547 function _toLong(val) {
548     var ret = parseInt(val);
549     return isNaN(ret) ? (val === true ? 1 : 0) : ret;
550 }
551
552 Converter.prototype.toLong = function(val, nullable) {
553     return _nullableGeneric(_toLong, nullable, val);
554 };
555
556 function _toLongLong(val) {
557     // According to WebIDL specification this will not be a precise representation
558     // of requested val. We're converting the val to signed long and then pass it
559     // to C++ to get the value in required range.
560     return native_.getResultObject(
561         native_.callSync('UtilsToLongLong', {
562             n: _toLong(val)
563         })
564     );
565 }
566
567 Converter.prototype.toLongLong = function(val, nullable) {
568     return _nullableGeneric(_toLongLong, nullable, val);
569 };
570
571 function _toUnsignedLong(val) {
572     return _toLong(val) >>> 0;
573 }
574
575 Converter.prototype.toUnsignedLong = function(val, nullable) {
576     return _nullableGeneric(_toUnsignedLong, nullable, val);
577 };
578
579 function _toUnsignedLongLong(val) {
580     // According to WebIDL specification this will not be a precise representation
581     // of requested val. We're converting the val to signed long and then pass it
582     // to C++ to get the value in required range.
583     return native_.getResultObject(
584         native_.callSync('UtilsToUnsignedLongLong', {
585             n: _toLong(val)
586         })
587     );
588 }
589
590 Converter.prototype.toUnsignedLongLong = function(val, nullable) {
591     return _nullableGeneric(_toUnsignedLongLong, nullable, val);
592 };
593
594 function _toShort(val) {
595     return ((_toLong(val) + 32768) & 0xffff) - 32768;
596 }
597
598 Converter.prototype.toShort = function(val, nullable) {
599     return _nullableGeneric(_toShort, nullable, val);
600 };
601
602 function _toUnsignedShort(val) {
603     return Math.abs(_toLong(val)) & 0xffff;
604 }
605
606 Converter.prototype.toUnsignedShort = function(val, nullable) {
607     return _nullableGeneric(_toUnsignedShort, nullable, val);
608 };
609
610 function _toByte(val) {
611     return ((_toLong(val) + 128) & 0xff) - 128;
612 }
613
614 Converter.prototype.toByte = function(val, nullable) {
615     return _nullableGeneric(_toByte, nullable, val);
616 };
617
618 function _toOctet(val) {
619     return _toLong(val) & 0xff;
620 }
621
622 Converter.prototype.toOctet = function(val, nullable) {
623     return _nullableGeneric(_toOctet, nullable, val);
624 };
625
626 function _toDouble(val) {
627     var ret = Number(val);
628     if (isNaN(ret) || !isFinite(ret)) {
629         throw new WebAPIException(
630             WebAPIException.TYPE_MISMATCH_ERR,
631             'Cannot convert ' + String(val) + ' to double.'
632         );
633     }
634     return ret;
635 }
636
637 Converter.prototype.toDouble = function(val, nullable) {
638     return _nullableGeneric(_toDouble, nullable, val);
639 };
640
641 function _toString(val) {
642     return String(val);
643 }
644
645 Converter.prototype.toString = function(val, nullable) {
646     return _nullableGeneric(_toString, nullable, val);
647 };
648
649 function _toPlatformObject(val, types) {
650     var t;
651
652     if (_type.isArray(types)) {
653         t = types;
654     } else {
655         t = [types];
656     }
657
658     if (_type.isArray(val)) {
659         throw new WebAPIException(
660             WebAPIException.TYPE_MISMATCH_ERR,
661             'Cannot convert ' + String(val) + ' to ' + String(t[0].name) + '.'
662         );
663     }
664
665     var match = false;
666     for (var i = 0; i < t.length; ++i) {
667         if (val instanceof t[i]) {
668             return val;
669         }
670     }
671
672     throw new WebAPIException(
673         WebAPIException.TYPE_MISMATCH_ERR,
674         'Cannot convert ' + String(val) + ' to ' + String(t[0].name) + '.'
675     );
676 }
677
678 Converter.prototype.toPlatformObject = function(val, types, nullable) {
679     return _nullableGeneric(_toPlatformObject, nullable, val, types);
680 };
681
682 function _toFunction(val) {
683     if (_type.isFunction(val)) {
684         return val;
685     }
686
687     throw new WebAPIException(
688         WebAPIException.TYPE_MISMATCH_ERR,
689         'Cannot convert ' + String(val) + ' to function.'
690     );
691 }
692
693 Converter.prototype.toFunction = function(val, nullable) {
694     return _nullableGeneric(_toFunction, nullable, val);
695 };
696
697 function _toArray(val) {
698     if (_type.isArray(val)) {
699         return val;
700     }
701
702     throw new WebAPIException(
703         WebAPIException.TYPE_MISMATCH_ERR,
704         'Cannot convert ' + String(val) + ' to array.'
705     );
706 }
707
708 Converter.prototype.toArray = function(val, nullable) {
709     return _nullableGeneric(_toArray, nullable, val);
710 };
711
712 function _toDictionary(val) {
713     if (_type.isObject(val) || _type.isFunction(val)) {
714         return val;
715     }
716
717     throw new WebAPIException(
718         WebAPIException.TYPE_MISMATCH_ERR,
719         'Cannot convert ' + String(val) + ' to dictionary.'
720     );
721 }
722
723 Converter.prototype.toDictionary = function(val, nullable) {
724     return _nullableGeneric(_toDictionary, nullable, val);
725 };
726
727 function _toEnum(val, e) {
728     var v = _toString(val);
729     if (_type.arrayContains(e, v)) {
730         return v;
731     }
732
733     throw new WebAPIException(
734         WebAPIException.TYPE_MISMATCH_ERR,
735         'Cannot convert ' + v + ' to enum.'
736     );
737 }
738
739 Converter.prototype.toEnum = function(val, e, nullable) {
740     return _nullableGeneric(_toEnum, nullable, val, e);
741 };
742
743 var _converter = new Converter();
744
745 /////////////////////////////////////////////////////////////////////////////
746 /** @constructor */
747 var Validator = function() {
748     this.Types = {
749         BOOLEAN: 'BOOLEAN',
750         LONG: 'LONG',
751         LONG_LONG: 'LONG_LONG',
752         UNSIGNED_LONG: 'UNSIGNED_LONG',
753         UNSIGNED_LONG_LONG: 'UNSIGNED_LONG_LONG',
754         BYTE: 'BYTE',
755         OCTET: 'OCTET',
756         DOUBLE: 'DOUBLE',
757         STRING: 'STRING',
758         FUNCTION: 'FUNCTION',
759         DICTIONARY: 'DICTIONARY',
760         PLATFORM_OBJECT: 'PLATFORM_OBJECT',
761         LISTENER: 'LISTENER',
762         ARRAY: 'ARRAY',
763         ENUM: 'ENUM',
764         FILE_REFERENCE: 'FILE_REFERENCE',
765         SIMPLE_TYPE: 'SIMPLE_TYPE' // Boolean, Number or String
766     };
767 };
768
769 /**
770  * Verifies if arguments passed to function are valid.
771  *
772  * Description of expected arguments.
773  * This is an array of objects, each object represents one argument.
774  * First object in this array describes first argument, second object describes second
775  * argument, and so on.
776  * Object describing an argument needs to have two properties:
777  *   - name - name of the argument,
778  *   - type - type of the argument, only values specified in Validator.Types are allowed.
779  * Other properties, which may appear:
780  *   - optional - if set to value which evaluates to true, argument is optional
781  *   - nullable - if set to to true, argument may be set to null
782  *   - values - required in case of some objects, value depends on type
783  *   - validator - function which accepts a single parameter and returns true or false;
784  *                 if this property is present, this function will be executed,
785  *                 argument converted to expected type is going to be passed to this
786  *                 function
787  *
788  * @param {Array} a - arguments of a method
789  * @param {Array} d - description of expected arguments
790  * @return {Object} which holds all available arguments.
791  * @throws TypeMismatchError if arguments are not valid
792  *
793  * @code
794  * [
795  *   {
796  *     name: 'first',
797  *     type: 'aType'
798  *   }
799  * ]
800  * @code
801  * [
802  *   {
803  *     name: 'first',
804  *     type: 'aType',
805  *     optional: true
806  *   }
807  * ]
808  * @code
809  * [
810  *   {
811  *     name: 'first',
812  *     type: 'aType',
813  *     nullable: true
814  *   }
815  * ]
816  * @code
817  * [
818  *   {
819  *     name: 'first',
820  *     type: 'aType',
821  *     optional: true,
822  *     nullable: true
823  *   }
824  * ]
825  * @code
826  * [
827  *   {
828  *     name: 'first',
829  *     type: Validator.Types.PLATFORM_OBJECT,
830  *     values: ApplicationControl // type of platform object
831  *   }
832  * ]
833  * @code
834  * [
835  *   {
836  *     name: 'first',
837  *     type: Validator.Types.PLATFORM_OBJECT,
838  *     values: [Alarm, AlarmRelative, AlarmAbsolute] // accepted types
839  *   }
840  * ]
841  * @code
842  * [
843  *   {
844  *     name: 'first',
845  *     type: Validator.Types.LISTENER,
846  *     values: ['onsuccess', 'onfailure'] // array of callbacks' names
847  *   }
848  * ]
849  * @code
850  * [
851  *   {
852  *     name: 'first',
853  *     type: Validator.Types.ARRAY,
854  *     values: ApplicationControlData // type of each element in array,
855  *                                    // tested with instanceof
856  *   }
857  * ]
858  * @code
859  * [
860  *   {
861  *     name: 'first',
862  *     type: Validator.Types.ARRAY,
863  *     values: Validator.Types.DOUBLE // converts elements, only primitive
864  *                                    // types are supported
865  *   }
866  * ]
867  * @code
868  * [
869  *   {
870  *     name: 'first',
871  *     type: Validator.Types.ENUM,
872  *     values: ['SCREEN_DIM', 'SCREEN_NORMAL', 'CPU_AWAKE'] // array of allowed values
873  *   }
874  * ]
875  */
876 Validator.prototype.validateArgs = function(a, d) {
877     var args = { has: {} };
878
879     for (var i = 0; i < d.length; ++i) {
880         var name = d[i].name;
881         args.has[name] = i < a.length;
882
883         var optional = d[i].optional;
884         var nullable = d[i].nullable;
885         var val = a[i];
886
887         if (args.has[name] || !optional) {
888             var type = d[i].type;
889             var values = d[i].values;
890
891             switch (type) {
892             case this.Types.BOOLEAN:
893                 val = _converter.toBoolean(val, nullable);
894                 break;
895
896             case this.Types.LONG:
897                 val = _converter.toLong(val, nullable);
898                 break;
899
900             case this.Types.LONG_LONG:
901                 val = _converter.toLongLong(val, nullable);
902                 break;
903
904             case this.Types.UNSIGNED_LONG:
905                 val = _converter.toUnsignedLong(val, nullable);
906                 break;
907
908             case this.Types.UNSIGNED_LONG_LONG:
909                 val = _converter.toUnsignedLongLong(val, nullable);
910                 break;
911
912             case this.Types.BYTE:
913                 val = _converter.toByte(val, nullable);
914                 break;
915
916             case this.Types.OCTET:
917                 val = _converter.toOctet(val, nullable);
918                 break;
919
920             case this.Types.DOUBLE:
921                 val = _converter.toDouble(val, nullable);
922                 break;
923
924             case this.Types.STRING:
925                 val = _converter.toString(val, nullable);
926                 break;
927
928             case this.Types.FUNCTION:
929                 val = _converter.toFunction(val, nullable);
930                 break;
931
932             case this.Types.DICTIONARY:
933                 val = _converter.toDictionary(val, nullable);
934                 break;
935
936             case this.Types.PLATFORM_OBJECT:
937                 val = _converter.toPlatformObject(val, values, nullable);
938                 break;
939
940             case this.Types.LISTENER:
941                 if (_type.isNull(val)) {
942                     if (!nullable) {
943                         throw new WebAPIException(
944                             WebAPIException.TYPE_MISMATCH_ERR,
945                             'Argument "' + name + '" cannot be null.'
946                         );
947                     }
948                 } else {
949                     if (!_type.isObject(val)) {
950                         throw new WebAPIException(
951                             WebAPIException.TYPE_MISMATCH_ERR,
952                             'Argument "' + name + '" should be an object.'
953                         );
954                     }
955                     for (var ii = 0; ii < values.length; ++ii) {
956                         if (_type.hasProperty(val, values[ii])) {
957                             val[values[ii]] = _converter.toFunction(
958                                 val[values[ii]],
959                                 false
960                             );
961                         }
962                     }
963                 }
964                 break;
965
966             case this.Types.ARRAY:
967                 val = _converter.toArray(val, nullable);
968                 if (!_type.isNull(val) && values) {
969                     var func;
970
971                     switch (values) {
972                     case this.Types.BOOLEAN:
973                         func = _converter.toBoolean;
974                         break;
975
976                     case this.Types.LONG:
977                         func = _converter.toLong;
978                         break;
979
980                     case this.Types.LONG_LONG:
981                         func = _converter.toLongLong;
982                         break;
983
984                     case this.Types.UNSIGNED_LONG:
985                         func = _converter.toUnsignedLong;
986                         break;
987
988                     case this.Types.UNSIGNED_LONG_LONG:
989                         func = _converter.toUnsignedLongLong;
990                         break;
991
992                     case this.Types.BYTE:
993                         func = _converter.toByte;
994                         break;
995
996                     case this.Types.OCTET:
997                         func = _converter.toOctet;
998                         break;
999
1000                     case this.Types.DOUBLE:
1001                         func = _converter.toDouble;
1002                         break;
1003
1004                     case this.Types.STRING:
1005                         func = _converter.toString;
1006                         break;
1007
1008                     default:
1009                         func = function(val) {
1010                             if (!(val instanceof values)) {
1011                                 throw new WebAPIException(
1012                                     WebAPIException.TYPE_MISMATCH_ERR,
1013                                     'Items of array "' +
1014                                                 name +
1015                                                 '" should be of type: ' +
1016                                                 values +
1017                                                 '.'
1018                                 );
1019                             }
1020                             return val;
1021                         };
1022                     }
1023
1024                     for (var j = 0; j < val.length; ++j) {
1025                         val[j] = func(val[j]);
1026                     }
1027                 }
1028                 break;
1029
1030             case this.Types.ENUM:
1031                 val = _converter.toEnum(val, values, nullable);
1032                 break;
1033
1034             case this.Types.FILE_REFERENCE:
1035                 if (
1036                     _type.isObject(val) &&
1037                         'File' === val.constructor.name &&
1038                         val.fullPath
1039                 ) {
1040                     val = val.fullPath;
1041                 }
1042                 val = _converter.toString(val, nullable);
1043                 break;
1044
1045             case this.Types.SIMPLE_TYPE:
1046                 if (optional && _type.isUndefined(val)) {
1047                     break;
1048                 }
1049                 if (nullable && _type.isNull(val)) {
1050                     break;
1051                 }
1052                 if (
1053                     !_type.isBoolean(val) &&
1054                         !_type.isNumber(val) &&
1055                         !_type.isString(val)
1056                 ) {
1057                     throw new WebAPIException(
1058                         WebAPIException.TYPE_MISMATCH_ERR,
1059                         'Argument "' + name + '" should be boolean, number or string.'
1060                     );
1061                 }
1062                 break;
1063
1064             default:
1065                 throw new WebAPIException(
1066                     WebAPIException.TYPE_MISMATCH_ERR,
1067                     'Unknown type: "' + type + '".'
1068                 );
1069             }
1070
1071             var _validator = d[i].validator;
1072
1073             if (_type.isFunction(_validator) && !_validator(val)) {
1074                 throw new WebAPIException(
1075                     WebAPIException.TYPE_MISMATCH_ERR,
1076                     'Argument "' + name + '" did not pass additional validation.'
1077                 );
1078             }
1079
1080             args[name] = val;
1081         }
1082     }
1083
1084     return args;
1085 };
1086
1087 /**
1088  * Use this helper to ensure that constructor is invoked by "new" operator.
1089  *
1090  * @param {Object} obj
1091  * @param {Function} instance
1092  */
1093 Validator.prototype.isConstructorCall = function(obj, instance) {
1094     if (!(obj instanceof instance) || obj._previouslyConstructed) {
1095         // There is no TypeError exception in Tizen 2.3.0 API spec but it's required
1096         // by current TCTs. For Tizen compliance it's wrapped into WebAPIException.
1097         throw new WebAPIException(
1098             'TypeError',
1099             'Constructor cannot be called as function.'
1100         );
1101     }
1102
1103     Object.defineProperty(obj, '_previouslyConstructed', {
1104         value: true,
1105         writable: false,
1106         enumerable: false
1107     });
1108 };
1109
1110 /**
1111  * @deprecated Use isConstructorCall() instead.
1112  */
1113 Validator.prototype.validateConstructorCall = function(obj, instance) {
1114     this.isConstructorCall(obj, instance);
1115 };
1116
1117 var _validator = new Validator();
1118
1119 /////////////////////////////////////////////////////////////////////////////
1120 /** @constructor */
1121 var NativeManager = function(extension) {
1122     /**
1123      * @type {string}
1124      * @const
1125      */
1126     this.CALLBACK_ID_KEY = 'callbackId';
1127
1128     /**
1129      * @type {string}
1130      * @const
1131      */
1132     this.LISTENER_ID_KEY = 'listenerId';
1133
1134     /**
1135      * @type {Object}
1136      * @private
1137      */
1138     var extension_ = extension;
1139
1140     /**
1141      * @type {number}
1142      * @private
1143      */
1144     var replyId_ = 0;
1145
1146     /**
1147      * Map of async reply callbacks.
1148      *
1149      * @type {Object.<number, function>}
1150      * @protected
1151      */
1152     this.callbacks_ = {};
1153
1154     /**
1155      * Map of registered listeners.
1156      *
1157      * @type {Object.<string, function>}
1158      * @protected
1159      */
1160     this.listeners_ = {};
1161
1162     _validator.isConstructorCall(this, NativeManager);
1163
1164     // TODO: Remove mockup if WRT implements sendRuntimeMessage
1165     // This is temporary mockup!
1166     extension.sendRuntimeMessage =
1167         extension.sendRuntimeMessage ||
1168         function() {
1169             xwalk.utils.error('Runtime did not implement extension.sendRuntimeMessage!');
1170             throw new WebAPIException(
1171                 WebAPIException.UNKNOWN_ERR,
1172                 'Runtime did not implement extension.sendRuntimeMessage!'
1173             );
1174         };
1175
1176     extension.sendRuntimeAsyncMessage =
1177         extension.sendRuntimeAsyncMessage ||
1178         function() {
1179             xwalk.utils.error(
1180                 'Runtime did not implement extension.sendRuntimeAsyncMessage!'
1181             );
1182             throw new WebAPIException(
1183                 WebAPIException.UNKNOWN_ERR,
1184                 'Runtime did not implement extension.sendRuntimeAsyncMessage!'
1185             );
1186         };
1187
1188     extension.sendRuntimeSyncMessage =
1189         extension.sendRuntimeSyncMessage ||
1190         function() {
1191             xwalk.utils.error(
1192                 'Runtime did not implement extension.sendRuntimeSyncMessage!'
1193             );
1194             throw new WebAPIException(
1195                 WebAPIException.UNKNOWN_ERR,
1196                 'Runtime did not implement extension.sendRuntimeSyncMessage!'
1197             );
1198         };
1199
1200     // check extension prototype
1201     if (
1202         !extension ||
1203         !extension.internal ||
1204         !_type.isFunction(extension.postMessage) ||
1205         !_type.isFunction(extension.internal.sendSyncMessage) ||
1206         !_type.isFunction(extension.internal.sendSyncMessageWithBinaryReply) ||
1207         !_type.isFunction(extension.internal.sendSyncMessageWithStringReply) ||
1208         !_type.isFunction(extension.sendRuntimeMessage) ||
1209         !_type.isFunction(extension.sendRuntimeAsyncMessage) ||
1210         !_type.isFunction(extension.sendRuntimeSyncMessage) ||
1211         !_type.isFunction(extension.setMessageListener)
1212     ) {
1213         throw new WebAPIException(
1214             WebAPIException.TYPE_MISMATCH_ERR,
1215             'Wrong extension object passed'
1216         );
1217     }
1218
1219     Object.defineProperties(this, {
1220         nextReplyId: {
1221             get: function() {
1222                 return ++replyId_;
1223             },
1224             enumerable: false
1225         },
1226         extension: {
1227             get: function() {
1228                 return extension_;
1229             },
1230             enumerable: true
1231         }
1232     });
1233
1234     extension_.setMessageListener(
1235         function(json) {
1236             try {
1237                 var msg = JSON_.parse(json);
1238             } catch (error) {
1239                 // Because of special handling of power lock in chromium, the special
1240                 // signals:
1241                 // - __DisableChromiumInternalPowerLock
1242                 // - __EnableChromiumInternalPowerLock
1243                 // could occur. In such cases we are silently ignroing those messages.
1244                 // TODO This is workaround for missing patch in chromium-efl package
1245                 //  which should handle this special message and don't forward it to
1246                 // webapi JS. After chromium-efl will be updated, below checking should
1247                 // be removed.
1248                 if (json.substring(0, 2) === '__') {
1249                     return;
1250                 }
1251                 xwalk.utils.error('Ignoring message - Invalid JSON received: ' + json);
1252                 return;
1253             }
1254             var id;
1255
1256             if (msg.hasOwnProperty(this.CALLBACK_ID_KEY)) {
1257                 id = msg[this.CALLBACK_ID_KEY];
1258                 delete msg[this.CALLBACK_ID_KEY];
1259
1260                 if (!_type.isFunction(this.callbacks_[id])) {
1261                     xwalk.utils.error('Wrong callback identifier. Ignoring message.');
1262                     return;
1263                 }
1264
1265                 var f = this.callbacks_[id];
1266                 setTimeout(function() {
1267                     try {
1268                         f(msg);
1269                     } catch (e) {
1270                         xwalk.utils.error('########## exception');
1271                         xwalk.utils.error(e);
1272                     }
1273                 }, 0);
1274                 delete this.callbacks_[id];
1275
1276                 return;
1277             }
1278
1279             if (msg.hasOwnProperty(this.LISTENER_ID_KEY)) {
1280                 id = msg[this.LISTENER_ID_KEY];
1281                 delete msg[this.LISTENER_ID_KEY];
1282
1283                 if (!_type.isFunction(this.listeners_[id])) {
1284                     xwalk.utils.error('Wrong listener identifier. Ignoring message.');
1285                     return;
1286                 }
1287
1288                 var f = this.listeners_[id];
1289                 setTimeout(function() {
1290                     try {
1291                         f(msg);
1292                     } catch (e) {
1293                         xwalk.utils.error('########## exception');
1294                         xwalk.utils.error(e);
1295                     }
1296                 }, 0);
1297
1298                 return;
1299             }
1300
1301             xwalk.utils.error(
1302                 'Missing callback or listener identifier. Ignoring message.'
1303             );
1304         }.bind(this)
1305     );
1306 };
1307
1308 NativeManager.prototype.call = function(cmd, args, callback) {
1309     args = args || {};
1310
1311     var replyId = this.nextReplyId;
1312     args[this.CALLBACK_ID_KEY] = replyId;
1313     this.callbacks_[replyId] = callback;
1314
1315     return this.callSync(cmd, args);
1316 };
1317
1318 NativeManager.prototype.callSync = function(cmd, args) {
1319     var request = JSON_.stringify({
1320         cmd: cmd,
1321         args: args || {}
1322     });
1323
1324     var response = this.extension.internal.sendSyncMessage(request);
1325     if (response === undefined) {
1326         /* C++ extension didn't set sync response using Instance::SendSyncReply */
1327         throw new WebAPIException(WebAPIException.ABORT_ERR, 'Internal error');
1328     }
1329     return JSON_.parse(response);
1330 };
1331
1332 NativeManager.prototype.callSyncWithBinaryAnswer = function (cmd, args) {
1333     var request = JSON_.stringify({
1334         __binaryAnswer: true,
1335         cmd: cmd,
1336         args: args || {}
1337     });
1338
1339     var response = this.extension.internal.sendSyncMessageWithBinaryReply(request);
1340     if (response === undefined) {
1341         /* C++ extension didn't set sync binary response using Instance::SendSyncBinaryReply */
1342         throw new WebAPIException(WebAPIException.ABORT_ERR, 'Internal error');
1343     }
1344     return response;
1345 };
1346
1347 NativeManager.prototype.callSyncBinaryWithJSONAnswer = function(uint8array_data) {
1348     // method id should be coded as first byte, refer to:
1349     // extension.cc - ParsedInstance:: HandleBinaryMessage
1350
1351     var response = this.extension.internal.sendSyncMessage(uint8array_data);
1352     if (response === undefined) {
1353         /* C++ extension didn't set sync response using Instance::SendSyncReply */
1354         throw new WebAPIException(WebAPIException.ABORT_ERR, 'Internal error');
1355     }
1356     return JSON_.parse(response);
1357 };
1358
1359 NativeManager.prototype.sendRuntimeMessage = function(msg, body) {
1360     return this.extension.sendRuntimeMessage(msg, body || '');
1361 };
1362
1363 NativeManager.prototype.sendRuntimeAsyncMessage = function(msg, body, callback) {
1364     var handler = function(response) {
1365         if (_type.isFunction(callback)) {
1366             var result = {};
1367             if ('success' === response.toLowerCase()) {
1368                 result.status = 'success';
1369             } else {
1370                 result.status = 'error';
1371                 result.error = new WebAPIException(
1372                     WebAPIException.UNKNOWN_ERR,
1373                     'Runtime message failure'
1374                 );
1375             }
1376             callback(result);
1377         }
1378     };
1379     return this.extension.sendRuntimeAsyncMessage(msg, body || '', handler);
1380 };
1381
1382 NativeManager.prototype.sendRuntimeSyncMessage = function(msg, body) {
1383     return this.extension.sendRuntimeSyncMessage(msg, body || '');
1384 };
1385
1386 NativeManager.prototype.addListener = function(name, callback) {
1387     if (!_type.isString(name) || !name.length) {
1388         throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR);
1389     }
1390
1391     this.listeners_[name] = callback;
1392 };
1393
1394 NativeManager.prototype.removeListener = function(name) {
1395     if (this.listeners_.hasOwnProperty(name)) {
1396         delete this.listeners_[name];
1397     }
1398 };
1399
1400 NativeManager.prototype.isListenerSet = function(name) {
1401     return this.listeners_.hasOwnProperty(name);
1402 };
1403
1404 NativeManager.prototype.isSuccess = function(result) {
1405     return result.status !== 'error';
1406 };
1407
1408 NativeManager.prototype.isFailure = function(result) {
1409     return !this.isSuccess(result);
1410 };
1411
1412 NativeManager.prototype.getResultObject = function(result) {
1413     return result.result;
1414 };
1415
1416 NativeManager.prototype.getErrorObject = function(result) {
1417     return new WebAPIException(result.error);
1418 };
1419
1420 /*
1421  * This function checks if the reported error's name is in valid_error_names.
1422  * If so, it is returned. Otherwise, default_error is returned.
1423  * valid_error_names should contain error names defined in the API reference
1424  * for the called function.
1425  */
1426 NativeManager.prototype.getErrorObjectAndValidate = function(
1427     result,
1428     valid_error_names,
1429     default_error
1430 ) {
1431     xwalk.utils.assert(
1432         Array.isArray(valid_error_names),
1433         'valid_error_names must be an Array. %s was passed instead',
1434         typeof valid_error_names
1435     );
1436     var error = new WebAPIException(result.error);
1437     if (valid_error_names.includes(error.name)) {
1438         return error;
1439     }
1440
1441     return default_error;
1442 };
1443
1444 NativeManager.prototype.callIfPossible = function(callback) {
1445     if (!_type.isNullOrUndefined(callback)) {
1446         callback.apply(callback, [].slice.call(arguments, 1));
1447     }
1448 };
1449
1450 NativeManager.prototype.callIfPossibleAndReturn = function(callback) {
1451     if (!_type.isNullOrUndefined(callback)) {
1452         return callback.apply(callback, [].slice.call(arguments, 1));
1453     }
1454 };
1455
1456 // WebAPIException and WebAPIError definition moved to Utils for compliance
1457 // reasons with blink-wrt environment.
1458 // In blink-wrt the original Tizen module is loaded, which is not providing
1459 // exception constructor.
1460 // As modules needs exceptions internally so they are loaded here for now.
1461 // See http://168.219.209.56/gerrit/#/c/23472/ for more details.
1462 // In future exception definition could be moved back to Tizen module.
1463 function __isObject(object) {
1464     return object instanceof _global.Object;
1465 }
1466
1467 function __isUndefined(object) {
1468     return object === void 0;
1469 }
1470
1471 function __isNumber(object) {
1472     return typeof object === 'number';
1473 }
1474
1475 // WARNING! This list should be in sync with the equivalent enum
1476 // located at tizen.h. Remember to update tizen.h if you change
1477 // something here.
1478 var errors = {
1479     NO_ERROR: 0,
1480     UNKNOWN_ERR: -1,
1481
1482     INDEX_SIZE_ERR: 1,
1483     DOMSTRING_SIZE_ERR: 2,
1484     HIERARCHY_REQUEST_ERR: 3,
1485     WRONG_DOCUMENT_ERR: 4,
1486     INVALID_CHARACTER_ERR: 5,
1487     NO_DATA_ALLOWED_ERR: 6,
1488     NO_MODIFICATION_ALLOWED_ERR: 7,
1489     NOT_FOUND_ERR: 8,
1490     NOT_SUPPORTED_ERR: 9,
1491     INUSE_ATTRIBUTE_ERR: 10,
1492     INVALID_STATE_ERR: 11,
1493     SYNTAX_ERR: 12,
1494     INVALID_MODIFICATION_ERR: 13,
1495     NAMESPACE_ERR: 14,
1496     INVALID_ACCESS_ERR: 15,
1497     VALIDATION_ERR: 16,
1498     TYPE_MISMATCH_ERR: 17,
1499     SECURITY_ERR: 18,
1500     NETWORK_ERR: 19,
1501     ABORT_ERR: 20,
1502     URL_MISMATCH_ERR: 21,
1503     QUOTA_EXCEEDED_ERR: 22,
1504     TIMEOUT_ERR: 23,
1505     INVALID_NODE_TYPE_ERR: 24,
1506     DATA_CLONE_ERR: 25,
1507
1508     // Error codes for these errors are not really defined anywhere.
1509     INVALID_VALUES_ERR: 100,
1510     IO_ERR: 101,
1511     SERVICE_NOT_AVAILABLE_ERR: 103,
1512     VERIFICATION_ERR: 105
1513 };
1514
1515 var code_to_name = {};
1516 code_to_name[errors['NO_ERROR']] = 'NoError';
1517 code_to_name[errors['UNKNOWN_ERR']] = 'UnknownError';
1518 code_to_name[errors['INDEX_SIZE_ERR']] = 'IndexSizeError';
1519 code_to_name[errors['DOMSTRING_SIZE_ERR']] = 'DOMStringSizeError';
1520 code_to_name[errors['HIERARCHY_REQUEST_ERR']] = 'HierarchyRequestError';
1521 code_to_name[errors['WRONG_DOCUMENT_ERR']] = 'WrongDocumentError';
1522 code_to_name[errors['INVALID_CHARACTER_ERR']] = 'InvalidCharacterError';
1523 code_to_name[errors['NO_DATA_ALLOWED_ERR']] = 'NoDataAllowedError';
1524 code_to_name[errors['NO_MODIFICATION_ALLOWED_ERR']] = 'NoModificationAllowedError';
1525 code_to_name[errors['NOT_FOUND_ERR']] = 'NotFoundError';
1526 code_to_name[errors['NOT_SUPPORTED_ERR']] = 'NotSupportedError';
1527 code_to_name[errors['INUSE_ATTRIBUTE_ERR']] = 'InuseAttributeError';
1528 code_to_name[errors['INVALID_STATE_ERR']] = 'InvalidStateError';
1529 code_to_name[errors['SYNTAX_ERR']] = 'SyntaxError';
1530 code_to_name[errors['INVALID_MODIFICATION_ERR']] = 'InvalidModificationError';
1531 code_to_name[errors['NAMESPACE_ERR']] = 'NamespaceError';
1532 code_to_name[errors['INVALID_ACCESS_ERR']] = 'InvalidAccessError';
1533 code_to_name[errors['VALIDATION_ERR']] = 'ValidationError';
1534 code_to_name[errors['TYPE_MISMATCH_ERR']] = 'TypeMismatchError';
1535 code_to_name[errors['SECURITY_ERR']] = 'SecurityError';
1536 code_to_name[errors['NETWORK_ERR']] = 'NetworkError';
1537 code_to_name[errors['ABORT_ERR']] = 'AbortError';
1538 code_to_name[errors['URL_MISMATCH_ERR']] = 'URLMismatchError';
1539 code_to_name[errors['QUOTA_EXCEEDED_ERR']] = 'QuotaExceededError';
1540 code_to_name[errors['TIMEOUT_ERR']] = 'TimeoutError';
1541 code_to_name[errors['INVALID_NODE_TYPE_ERR']] = 'InvalidNodeTypeError';
1542 code_to_name[errors['DATA_CLONE_ERR']] = 'DataCloneError';
1543
1544 code_to_name[errors['INVALID_VALUES_ERR']] = 'InvalidValuesError';
1545 code_to_name[errors['IO_ERR']] = 'IOError';
1546 code_to_name[errors['SERVICE_NOT_AVAILABLE_ERR']] = 'ServiceNotAvailableError';
1547 code_to_name[errors['VERIFICATION_ERR']] = 'VerificationError';
1548
1549 var name_to_code = {};
1550 Object.keys(errors).forEach(function(key) {
1551     name_to_code[code_to_name[errors[key]]] = errors[key];
1552 });
1553
1554 /**
1555  * Generic exception interface.
1556  *
1557  * @param {number} code 16-bit error code.
1558  * @param {string} message An error message that describes the details of
1559  *                          an encountered error.
1560  * @param {string} name An error type.
1561  */
1562 var WebAPIException = function(code, message, name) {
1563     var code_ = 0;
1564     var name_ = code_to_name[code];
1565     var message_ = 'Unknown error';
1566
1567     switch (arguments.length) {
1568     case 1:
1569         var error = arguments[0];
1570         if (__isObject(error)) {
1571             code_ = error.code;
1572             name_ = error.name;
1573             message_ = error.message;
1574             if (__isUndefined(code_) && !__isUndefined(name_))
1575                 code_ = name_to_code[name_];
1576             if (__isUndefined(name_) && !__isUndefined(code_))
1577                 name_ = code_to_name[code_];
1578         } else if (__isNumber(error)) {
1579             // backward compatibility with crosswalk implementation
1580             code_ = error;
1581             name_ = code_to_name[code];
1582             message_ = name_;
1583         }
1584         break;
1585     case 2:
1586         if (__isNumber(arguments[0])) {
1587             code_ = arguments[0];
1588             if (!__isUndefined(code_to_name[code_])) {
1589                 name_ = code_to_name[code_];
1590             }
1591         } else {
1592             name_ = String(arguments[0]);
1593             if (!__isUndefined(name_to_code[name_])) {
1594                 code_ = name_to_code[name_];
1595             }
1596         }
1597         message_ = String(arguments[1]);
1598         break;
1599     case 3:
1600         // backward compatibility with crosswalk implementation
1601         code_ = Number(arguments[0]);
1602         message_ = String(arguments[1]);
1603         name_ = String(arguments[2]);
1604         break;
1605     default:
1606         return;
1607     }
1608
1609     if (code_ > errors.DATA_CLONE_ERR) {
1610         code_ = 0;
1611     }
1612
1613     // attributes
1614     Object.defineProperties(this, {
1615         code: { value: code_, writable: false, enumerable: true },
1616         name: { value: name_, writable: false, enumerable: true },
1617         message: { value: message_, writable: false, enumerable: true }
1618     });
1619
1620     this.constructor.prototype.__proto__ = Error.prototype;
1621     // V8-specific code
1622     Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);
1623 };
1624
1625 WebAPIException.prototype.toString = function() {
1626     return this.name + ': ' + this.message;
1627 };
1628
1629 var error_constants = {};
1630 for (var prop in errors) {
1631     error_constants[prop] = { value: errors[prop], writable: false, enumerable: true };
1632 }
1633 Object.defineProperties(WebAPIException, error_constants);
1634 Object.defineProperties(WebAPIException.prototype, error_constants);
1635
1636 // Modules should be 'undefined' if related feature is not supported.
1637 // Below function could be used for conditional definition of JS API exports as in below:
1638 // var relatedFeature = 'http://tizen.org/feature/network.bluetooth.audio.media';
1639 // xwalk.utils.exportModuleIfFeatureSupported(relatedFeature, exports,
1640 //                                            new MediaKeyManager(), undefined);
1641 function exportModuleIfFeatureSupported(relatedFeature, successExport, failureExport) {
1642     try {
1643         if (true === tizen.systeminfo.getCapability(relatedFeature)) {
1644             return successExport;
1645         }
1646     } catch (e) {
1647         // ignore errors
1648     }
1649     return failureExport;
1650 }
1651
1652 // Export WebAPIException and WebAPIError into global scope.
1653 // For compliance reasons their constructors should not be exported in tizen namespace,
1654 // but should be available internally to allow throwing exceptions from modules.
1655 var scope;
1656 if (typeof window !== 'undefined') {
1657     scope = window;
1658 } else if (typeof global !== 'undefined') {
1659     scope = global;
1660 } else if (typeof self !== 'undefined') {
1661     scope = self;
1662 }
1663 scope = scope || {};
1664 scope.WebAPIException = WebAPIException;
1665 scope.WebAPIError = WebAPIException;
1666
1667 // ArrayBufferParser is used for parsing the binary data returned from C++ as ArrayBuffer.
1668 // It expects contents like:
1669 // - JSON coded as utf-8 format (1 byte per element)
1670 // - binary data of any ArrayBuffer (M bytes per element)
1671 // WARNING: for convienence of parsing data in JS layer, we always provide
1672 // JSON part (size + coded JSON) which length is a multiplication of 8 to
1673 // handle 1, 2, 4 and 8 bytes data smoothly.
1674 // Refer to common::tools::ReportSuccessToBinary, ReportErrorToBinary and
1675 // ReportDataToBinary methods
1676 function ArrayBufferParser(array) {
1677     this.idx_ = 0;
1678     this.typedArray_ = new Uint8Array(array);
1679 }
1680
1681 ArrayBufferParser.prototype.getJSON = function () {
1682     if (this.idx_ >= this.typedArray_.length) {
1683         console.log('No more data');
1684         return;
1685     }
1686     // expecting 4 bytes size (N) starting from startIdx
1687     // N bytes of data
1688
1689     var N =
1690         (this.typedArray_[this.idx_] << 24) +
1691         (this.typedArray_[this.idx_ + 1] << 16) +
1692         (this.typedArray_[this.idx_ + 2] << 8) +
1693         this.typedArray_[this.idx_ + 3];
1694
1695     var subArray = this.typedArray_.slice(this.idx_ + 4, this.idx_ + 4 + N);
1696     var resultJson = JSON.parse(Utils.prototype.ArrayToString(subArray));
1697     this.idx_ += N + 4;
1698     return resultJson;
1699 };
1700
1701 ArrayBufferParser.prototype.getData = function (ArrayType) {
1702     if (this.idx_ >= this.typedArray_.length) {
1703         console.log('No more data');
1704         return;
1705     }
1706     // Expecting 8 bytes size (N) starting from startIdx
1707     // N bytes of data. Refer to common::tools::PushJSONToBinary comment.
1708     var N =
1709         (this.typedArray_[this.idx_] << 56) +
1710         (this.typedArray_[this.idx_ + 1] << 48) +
1711         (this.typedArray_[this.idx_ + 2] << 40) +
1712         (this.typedArray_[this.idx_ + 3] << 32) +
1713         (this.typedArray_[this.idx_ + 4] << 24) +
1714         (this.typedArray_[this.idx_ + 5] << 16) +
1715         (this.typedArray_[this.idx_ + 6] << 8) +
1716         this.typedArray_[this.idx_ + 7];
1717
1718     // As ArrayType constructor expects the size in elements (not bytes),
1719     // N need to be adjusted
1720     N /= ArrayType.BYTES_PER_ELEMENT;
1721     // getting N elements starting from idx_ + 8
1722     var subArray = new ArrayType(this.typedArray_.buffer, this.idx_ + 8, N);
1723     this.idx_ += N + 8;
1724     return subArray;
1725 };
1726
1727 Utils.prototype.dateConverter = _dateConverter;
1728 Utils.prototype.type = _type;
1729 Utils.prototype.converter = _converter;
1730 Utils.prototype.validator = _validator;
1731 Utils.prototype.NativeManager = NativeManager;
1732 Utils.prototype.CommonListenerManager = CommonListenerManager;
1733 Utils.prototype.exportModuleIfFeatureSupported = exportModuleIfFeatureSupported;
1734 Utils.prototype.ArrayBufferParser = ArrayBufferParser;
1735
1736 var native_ = new NativeManager(extension);
1737
1738 exports.utils = new Utils();
1739
1740 Object.freeze(exports);
1741 Object.freeze(exports.utils);
1742 Object.freeze(Utils.prototype);
1743 Object.freeze(NativeManager.prototype);
1744 Object.freeze(CommonListenerManager.prototype);