Merge branch 'tizen_3.0' into tizen_4.0
[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 }
17 else if (typeof global != 'undefined') {
18   _global = global;
19 }
20
21 /**
22  * @deprecated Used only by validateArguments()
23  */
24 var signature_to_type = {
25   'n': 'number',
26   'f': 'function',
27   'b': 'boolean',
28   's': 'string',
29   'o': 'object'
30 };
31
32 var DateConverter = function() {};
33
34 DateConverter.prototype.toTZDate = function(v, isAllDay) {
35   if (typeof v === 'number') {
36     v = {
37         UTCTimestamp: v
38     };
39     isAllDay = false;
40   }
41
42   if (!(v instanceof _global.Object)) {
43     return v;
44   }
45
46   if (isAllDay) {
47     return new tizen.TZDate(v.year, v.month - 1, v.day,
48         null, null, null, null, v.timezone || null);
49   } else {
50     return new tizen.TZDate(new Date(v.UTCTimestamp * 1000));
51   }
52 };
53
54 DateConverter.prototype.fromTZDate = function(v) {
55   if (!tizen.TZDate || !(v instanceof tizen.TZDate)) {
56     return v;
57   }
58
59   return {
60     year: v.getFullYear(),
61     month: v.getMonth(),
62     day: v.getDate(),
63     timezone: v.getTimezone(),
64     UTCTimestamp: v._utcTimestamp / 1000
65   };
66
67 };
68
69 var _dateConverter = new DateConverter();
70
71 /** @constructor */
72 function Utils() {
73    
74   /**
75    * Cynara(since tizen 3.0) only support native privilege. 
76    * simply web privilege convert native privilege for checking access. 
77    */
78   var privilege = {
79     ACCOUNT_READ: 'http://tizen.org/privilege/account.read',
80     ACCOUNT_WRITE: 'http://tizen.org/privilege/account.write',
81     ALARM: 'http://tizen.org/privilege/alarm.get',
82     APPLICATION_INFO: 'http://tizen.org/privilege/application.info',
83     APPLICATION_LAUNCH: 'http://tizen.org/privilege/application.launch',
84     APPMANAGER_CERTIFICATE: 'http://tizen.org/privilege/appmanager.certificate',
85     APPMANAGER_KILL: 'http://tizen.org/privilege/appmanager.kill',
86     BLUETOOTH_ADMIN: 'http://tizen.org/privilege/bluetooth.admin',
87     BLUETOOTH_GAP: 'http://tizen.org/privilege/bluetooth.gap',
88     BLUETOOTH_HEALTH: 'http://tizen.org/privilege/bluetooth.health',
89     BLUETOOTH_SPP: 'http://tizen.org/privilege/bluetooth.spp',
90     BLUETOOTHMANAGER: 'http://tizen.org/privilege/bluetoothmanager',
91     BLUETOOTH: 'http://tizen.org/privilege/bluetooth',
92     BOOKMARK_READ: 'http://tizen.org/privilege/bookmark.read',
93     BOOKMARK_WRITE: 'http://tizen.org/privilege/bookmark.write',
94     CALENDAR_READ: 'http://tizen.org/privilege/calendar.read',
95     CALENDAR_WRITE: 'http://tizen.org/privilege/calendar.write',
96     CALLHISTORY_READ: 'http://tizen.org/privilege/callhistory.read',
97     CALLHISTORY_WRITE: 'http://tizen.org/privilege/callhistory.write',
98     CONTACT_READ: 'http://tizen.org/privilege/contact.read',
99     CONTACT_WRITE: 'http://tizen.org/privilege/contact.write',
100     CONTENT_READ: 'http://tizen.org/privilege/content.write',
101     CONTENT_WRITE: 'http://tizen.org/privilege/content.write',
102     DATACONTROL_CONSUMER: 'http://tizen.org/privilege/datacontrol.consumer',
103     DATASYNC: 'http://tizen.org/privilege/datasync',
104     DOWNLOAD: 'http://tizen.org/privilege/download',
105     FILESYSTEM_READ: 'http://tizen.org/privilege/filesystem.read',
106     FILESYSTEM_WRITE: 'http://tizen.org/privilege/filesystem.write',
107     HAPTIC: 'http://tizen.org/privilege/haptic',
108     HEALTHINFO: 'http://tizen.org/privilege/healthinfo',
109     INTERNET: 'http://tizen.org/privilege/internet',
110     LED: 'http://tizen.org/privilege/led',
111     LOCATION: 'http://tizen.org/privilege/location',
112     MEDIACONTROLLER_SERVER: 'http://tizen.org/privilege/mediacontroller.server',
113     MEDIACONTROLLER_CLIENT: 'http://tizen.org/privilege/mediacontroller.client',
114     MESSAGING_READ: 'http://tizen.org/privilege/messaging.read',
115     MESSAGING_WRITE: 'http://tizen.org/privilege/messaging.write',
116     NETWORKBEARERSELECTION: 'http://tizen.org/privilege/networkbearerselection',
117     NFC_ADMIN: 'http://tizen.org/privilege/nfc.admin',
118     NFC_CARDEMULATION: 'http://tizen.org/privilege/nfc.cardemulation',
119     NFC_COMMON: 'http://tizen.org/privilege/nfc.common',
120     NFC_P2P: 'http://tizen.org/privilege/nfc.p2p',
121     NFC_TAG: 'http://tizen.org/privilege/nfc.tag',
122     NOTIFICATION: 'http://tizen.org/privilege/notification',
123     PACKAGE_INFO: 'http://tizen.org/privilege/packagemanager.info',
124     PACKAGEMANAGER_INSTALL: 'http://tizen.org/privilege/packagemanager.install',
125     POWER: 'http://tizen.org/privilege/power',
126     PUSH: 'http://tizen.org/privilege/push',
127     SECUREELEMENT: 'http://tizen.org/privilege/secureelement',
128     SETTING_ADMIN: 'http://tizen.org/privilege/systemsettings.admin',
129     SETTING: 'http://tizen.org/privilege/setting',
130     SYSTEM: 'http://tizen.org/privilege/system',
131     SYSTEMMANAGER: 'http://tizen.org/privilege/systemmanager',
132     TELEPHONY: 'http://tizen.org/privilege/telephony',
133     VOLUME_SET: 'http://tizen.org/privilege/volume.set',
134     WEBSETTING: 'http://tizen.org/privilege/websetting',
135     TV_INPUT_DEVICE: 'http://tizen.org/privilege/tv.inputdevice'
136   };
137
138   Object.freeze(privilege);
139
140   Object.defineProperty(this, 'privilege', {
141     value: privilege,
142     writable: false,
143     enumerable: true,
144     configurable: false
145   });
146 }
147
148 Utils.prototype.error = console.error.bind(console);
149 Utils.prototype.warn = console.warn.bind(console);
150 Utils.prototype.log = _enableJsLogs ? console.log.bind(console) : function(){};
151
152 Utils.prototype.global = _global;
153
154 Utils.prototype.repackFilter = function(filter) {
155   if (filter instanceof tizen.AttributeFilter) {
156     return {
157       filterType: 'AttributeFilter',
158       attributeName: filter.attributeName,
159       matchFlag: filter.matchFlag,
160       matchValue: _dateConverter.fromTZDate(filter.matchValue)
161     };
162   }
163   if (filter instanceof tizen.AttributeRangeFilter) {
164     return {
165       filterType: 'AttributeRangeFilter',
166       attributeName: filter.attributeName,
167       initialValue: _dateConverter.fromTZDate(filter.initialValue),
168       endValue: _dateConverter.fromTZDate(filter.endValue)
169     };
170   }
171   if (filter instanceof tizen.CompositeFilter) {
172     var _f = [];
173     var filters = filter.filters;
174
175     for (var i = 0; i < filters.length; ++i) {
176       _f.push(this.repackFilter(filters[i]));
177     }
178
179     return {
180       filterType: 'CompositeFilter',
181       type: filter.type,
182       filters: _f
183     };
184   }
185
186   return null;
187 };
188
189 /**
190  * @deprecated You should use xwalk.utils.validator.validateMethod() instead.
191  */
192 Utils.prototype.validateArguments = function(signature, args) {
193   var full_args = Array.prototype.slice.call(args);
194
195   // After '?' everything is optional.
196   var mandatory_len = signature.indexOf('?') === -1 ? signature.length : signature.indexOf('?');
197
198   if (full_args.length < mandatory_len)
199     return false;
200
201   // Mandatory arguments.
202   for (var i = 0; i < mandatory_len; i++) {
203     if (typeof full_args[i] !== signature_to_type[signature[i]] || full_args[i] === null)
204       return false;
205   }
206
207   // Optional args may be null.
208   for (var i = mandatory_len; i < full_args.length && i < signature.length - 1; i++) {
209     if (full_args[i] !== null && typeof full_args[i] !== signature_to_type[signature[i + 1]])
210       return false;
211   }
212
213   return true;
214 };
215
216 Utils.prototype.validateObject = function(object, signature, attributes) {
217   for (var i = 0; i < signature.length; i++) {
218     if (object.hasOwnProperty(attributes[i]) &&
219         typeof object[attributes[i]] !== signature_to_type[signature[i]]) {
220       return false;
221     }
222   }
223
224   return true;
225 };
226
227 Utils.prototype.getPkgApiVersion = function() {
228   var result = native_.callSync('Utils_getPkgApiVersion');
229   if (native_.isFailure(result)) {
230     throw native_.getErrorObject(result);
231   }
232   return native_.getResultObject(result);
233 };
234
235 Utils.prototype.checkPrivilegeAccess = function(privilege) {
236   var result = native_.callSync('Utils_checkPrivilegeAccess', {
237     privilege : _toString(privilege),
238   });
239
240   if (native_.isFailure(result)) {
241     throw native_.getErrorObject(result);
242   }
243 };
244
245 Utils.prototype.isAppVersionEarlierThan= function(ver) {
246   var app_ver = this.getPkgApiVersion();
247
248   var arr_ver = ver.split(".");   // reference version
249   var arr_app_ver = app_ver.split(".");  // application version
250   var num_ver;
251   var num_app;
252
253   var i;
254   var length = Math.min(arr_ver.length, arr_app_ver.length);
255   for (i = 0; i < length; i++) {
256     num_ver = parseInt(arr_ver[i]);
257     num_app = parseInt(arr_app_ver[i]);
258     if (num_app < num_ver) {
259       return true;
260     } else if (num_app > num_ver) {
261       return false;
262     }
263   }
264
265   if (arr_ver.length > arr_app_ver.length) {
266     return true;
267   }
268   return false;
269 }
270
271 Utils.prototype.checkPrivilegeAccess4Ver = function(new_ver, new_priv, old_priv) {
272   if (!this.isAppVersionEarlierThan(new_ver)) {
273     this.checkPrivilegeAccess(new_priv);
274   } else if (old_priv != undefined) {
275     this.checkPrivilegeAccess(old_priv);
276   }
277 }
278
279 Utils.prototype.checkBackwardCompabilityPrivilegeAccess = function(current_privilege, previous_privilege) {
280   var result = native_.callSync('Utils_checkBackwardCompabilityPrivilegeAccess', {
281     current_privilege : _toString(current_privilege),
282     previous_privilege : _toString(previous_privilege),
283   });
284
285   if (native_.isFailure(result)) {
286     throw native_.getErrorObject(result);
287   }
288 };
289
290 Utils.prototype.checkProfile = function() {
291   var result = native_.callSync('Utils_checkProfile', {});
292
293   return native_.getResultObject(result);
294 };
295
296
297 /////////////////////////////////////////////////////////////////////////////
298 /** @constructor */
299 var Type = function() {};
300
301 Type.prototype.isBoolean = function(obj) {
302   return typeof obj === 'boolean';
303 };
304
305 Type.prototype.isObject = function(obj) {
306   return (null !== obj && typeof obj === 'object' && !this.isArray(obj));
307 };
308
309 Type.prototype.isArray = function(obj) {
310   return Array.isArray(obj);
311 };
312
313 Type.prototype.isFunction = function(obj) {
314   return typeof obj === 'function';
315 };
316
317 Type.prototype.isNumber = function(obj) {
318   return typeof obj === 'number';
319 };
320
321 Type.prototype.isString = function(obj) {
322   return typeof obj === 'string';
323 };
324
325 Type.prototype.isDate = function(obj) {
326   return obj instanceof Date;
327 };
328
329 Type.prototype.isNull = function(obj) {
330   return obj === null;
331 };
332
333 Type.prototype.isNullOrUndefined = function(obj) {
334   return (obj === null || obj === undefined);
335 };
336
337 Type.prototype.isUndefined = function(obj) {
338   return obj === void 0;
339 };
340
341 Type.prototype.isA = function(obj, type) {
342   var clas = Object.prototype.toString.call(obj).slice(8, -1);
343   return (obj !== undefined) && (obj !== null) && (clas === type);
344 };
345
346 Type.prototype.isEmptyObject = function(obj) {
347   for (var property in obj) {
348     if (obj.hasOwnProperty(property)) {
349       return false;
350     }
351   }
352   return true;
353 };
354
355 Type.prototype.hasProperty = function(obj, prop) {
356   return prop in obj;
357 };
358
359 Type.prototype.arrayContains = function(arr, value) {
360   return (arr.indexOf(value) > -1);
361 };
362
363 Type.prototype.getValues = function(obj) {
364   var ret = [];
365   for (var key in obj) {
366     if (obj.hasOwnProperty(key)) {
367       ret.push(obj[key]);
368     }
369   }
370   return ret;
371 };
372
373 var _type = new Type();
374
375
376
377 /////////////////////////////////////////////////////////////////////////////
378 /** @constructor */
379 var Converter = function() {};
380
381 function _nullableGeneric(func, nullable, val) {
382   if (_type.isNull(val) && nullable === true) {
383     return val;
384   } else {
385     return func.apply(null, [].slice.call(arguments, 2));
386   }
387 }
388
389 function _toBoolean(val) {
390   return Boolean(val);
391 }
392
393 Converter.prototype.toBoolean = function(val, nullable) {
394   return _nullableGeneric(_toBoolean, nullable, val);
395 };
396
397 function _toLong(val) {
398   var ret = parseInt(val);
399   return isNaN(ret) ? (val === true ? 1 : 0) : ret;
400 }
401
402 Converter.prototype.toLong = function(val, nullable) {
403   return _nullableGeneric(_toLong, nullable, val);
404 };
405
406 function _toLongLong(val) {
407   // According to WebIDL specification this will not be a precise representation
408   // of requested val. We're converting the val to signed long and then pass it
409   // to C++ to get the value in required range.
410   return native_.getResultObject(native_.callSync('Utils_toLongLong', {
411     n : _toLong(val)
412   }));
413 }
414
415 Converter.prototype.toLongLong = function(val, nullable) {
416   return _nullableGeneric(_toLongLong, nullable, val);
417 };
418
419 function _toUnsignedLong(val) {
420   return _toLong(val) >>> 0;
421 }
422
423 Converter.prototype.toUnsignedLong = function(val, nullable) {
424   return _nullableGeneric(_toUnsignedLong, nullable, val);
425 };
426
427 function _toUnsignedLongLong(val) {
428   // According to WebIDL specification this will not be a precise representation
429   // of requested val. We're converting the val to signed long and then pass it
430   // to C++ to get the value in required range.
431   return native_.getResultObject(native_.callSync('Utils_toUnsignedLongLong', {
432     n : _toLong(val)
433   }));
434 }
435
436 Converter.prototype.toUnsignedLongLong = function(val, nullable) {
437   return _nullableGeneric(_toUnsignedLongLong, nullable, val);
438 };
439
440 function _toShort(val) {
441   return ((_toLong(val) + 32768) & 0xFFFF) - 32768;
442 }
443
444 Converter.prototype.toShort = function(val, nullable) {
445   return _nullableGeneric(_toShort, nullable, val);
446 };
447
448 function _toUnsignedShort(val) {
449   return (Math.abs(_toLong(val)) & 0xFFFF);
450 }
451
452 Converter.prototype.toUnsignedShort = function(val, nullable) {
453   return _nullableGeneric(_toUnsignedShort, nullable, val);
454 };
455
456 function _toByte(val) {
457   return ((_toLong(val) + 128) & 0xFF) - 128;
458 }
459
460 Converter.prototype.toByte = function(val, nullable) {
461   return _nullableGeneric(_toByte, nullable, val);
462 };
463
464 function _toOctet(val) {
465   return _toLong(val) & 0xFF;
466 }
467
468 Converter.prototype.toOctet = function(val, nullable) {
469   return _nullableGeneric(_toOctet, nullable, val);
470 };
471
472 function _toDouble(val) {
473   var ret = Number(val);
474   if (isNaN(ret) || !isFinite(ret)) {
475     throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
476         'Cannot convert ' + String(val) + ' to double.');
477   }
478   return ret;
479 }
480
481 Converter.prototype.toDouble = function(val, nullable) {
482   return _nullableGeneric(_toDouble, nullable, val);
483 };
484
485 function _toString(val) {
486   return String(val);
487 }
488
489 Converter.prototype.toString = function(val, nullable) {
490   return _nullableGeneric(_toString, nullable, val);
491 };
492
493 function _toPlatformObject(val, types) {
494   var v;
495   var t;
496   if (_type.isArray(val)) {
497     v = val;
498   } else {
499     v = [val];
500   }
501
502   if (_type.isArray(types)) {
503     t = types;
504   } else {
505     t = [types];
506   }
507   var match = false;
508   for (var i = 0; i < t.length; ++i) {
509     for (var j = 0; j < v.length; ++j) {
510       match = match || (v[j] instanceof t[i]);
511     }
512   }
513   if (match) {
514     return val;
515   }
516
517   throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
518       'Cannot convert ' + String(val) + ' to ' + String(t[0].name) + '.');
519 }
520
521 Converter.prototype.toPlatformObject = function(val, types, nullable) {
522   return _nullableGeneric(_toPlatformObject, nullable, val, types);
523 };
524
525 function _toFunction(val) {
526   if (_type.isFunction(val)) {
527     return val;
528   }
529
530   throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
531       'Cannot convert ' + String(val) + ' to function.');
532 }
533
534 Converter.prototype.toFunction = function(val, nullable) {
535   return _nullableGeneric(_toFunction, nullable, val);
536 };
537
538 function _toArray(val) {
539   if (_type.isArray(val)) {
540     return val;
541   }
542
543   throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
544       'Cannot convert ' + String(val) + ' to array.');
545 }
546
547 Converter.prototype.toArray = function(val, nullable) {
548   return _nullableGeneric(_toArray, nullable, val);
549 };
550
551 function _toDictionary(val) {
552   if (_type.isObject(val) || _type.isFunction(val)) {
553     return val;
554   }
555
556   throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
557       'Cannot convert ' + String(val) + ' to dictionary.');
558 }
559
560 Converter.prototype.toDictionary = function(val, nullable) {
561   return _nullableGeneric(_toDictionary, nullable, val);
562 };
563
564 function _toEnum(val, e) {
565   var v = _toString(val);
566   if (_type.arrayContains(e, v)) {
567     return v;
568   }
569
570   throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
571       'Cannot convert ' + v + ' to enum.');
572 }
573
574 Converter.prototype.toEnum = function(val, e, nullable) {
575   return _nullableGeneric(_toEnum, nullable, val, e);
576 };
577
578 var _converter = new Converter();
579
580 /////////////////////////////////////////////////////////////////////////////
581 /** @constructor */
582 var Validator = function() {
583   this.Types = {
584     BOOLEAN: 'BOOLEAN',
585     LONG: 'LONG',
586     LONG_LONG: 'LONG_LONG',
587     UNSIGNED_LONG: 'UNSIGNED_LONG',
588     UNSIGNED_LONG_LONG: 'UNSIGNED_LONG_LONG',
589     BYTE: 'BYTE',
590     OCTET: 'OCTET',
591     DOUBLE: 'DOUBLE',
592     STRING: 'STRING',
593     FUNCTION: 'FUNCTION',
594     DICTIONARY: 'DICTIONARY',
595     PLATFORM_OBJECT: 'PLATFORM_OBJECT',
596     LISTENER: 'LISTENER',
597     ARRAY: 'ARRAY',
598     ENUM: 'ENUM',
599     FILE_REFERENCE: 'FILE_REFERENCE',
600     SIMPLE_TYPE: 'SIMPLE_TYPE'    // Boolean, Number or String
601   };
602 };
603
604
605 /**
606  * Verifies if arguments passed to function are valid.
607  *
608  * Description of expected arguments.
609  * This is an array of objects, each object represents one argument.
610  * First object in this array describes first argument, second object describes second
611  * argument, and so on.
612  * Object describing an argument needs to have two properties:
613  *   - name - name of the argument,
614  *   - type - type of the argument, only values specified in Validator.Types are allowed.
615  * Other properties, which may appear:
616  *   - optional - if set to value which evaluates to true, argument is optional
617  *   - nullable - if set to to true, argument may be set to null
618  *   - values - required in case of some objects, value depends on type
619  *   - validator - function which accepts a single parameter and returns true or false;
620  *                 if this property is present, this function will be executed,
621  *                 argument converted to expected type is going to be passed to this function
622  *
623  * @param {Array} a - arguments of a method
624  * @param {Array} d - description of expected arguments
625  * @return {Object} which holds all available arguments.
626  * @throws TypeMismatchError if arguments are not valid
627  *
628  * @code
629  * [
630  *   {
631  *     name: 'first',
632  *     type: 'aType'
633  *   }
634  * ]
635  * @code
636  * [
637  *   {
638  *     name: 'first',
639  *     type: 'aType',
640  *     optional: true
641  *   }
642  * ]
643  * @code
644  * [
645  *   {
646  *     name: 'first',
647  *     type: 'aType',
648  *     nullable: true
649  *   }
650  * ]
651  * @code
652  * [
653  *   {
654  *     name: 'first',
655  *     type: 'aType',
656  *     optional: true,
657  *     nullable: true
658  *   }
659  * ]
660  * @code
661  * [
662  *   {
663  *     name: 'first',
664  *     type: Validator.Types.PLATFORM_OBJECT,
665  *     values: ApplicationControl // type of platform object
666  *   }
667  * ]
668  * @code
669  * [
670  *   {
671  *     name: 'first',
672  *     type: Validator.Types.PLATFORM_OBJECT,
673  *     values: [Alarm, AlarmRelative, AlarmAbsolute] // accepted types
674  *   }
675  * ]
676  * @code
677  * [
678  *   {
679  *     name: 'first',
680  *     type: Validator.Types.LISTENER,
681  *     values: ['onsuccess', 'onfailure'] // array of callbacks' names
682  *   }
683  * ]
684  * @code
685  * [
686  *   {
687  *     name: 'first',
688  *     type: Validator.Types.ARRAY,
689  *     values: ApplicationControlData // type of each element in array,
690  *                                    // tested with instanceof
691  *   }
692  * ]
693  * @code
694  * [
695  *   {
696  *     name: 'first',
697  *     type: Validator.Types.ARRAY,
698  *     values: Validator.Types.DOUBLE // converts elements, only primitive types are supported
699  *   }
700  * ]
701  * @code
702  * [
703  *   {
704  *     name: 'first',
705  *     type: Validator.Types.ENUM,
706  *     values: ['SCREEN_DIM', 'SCREEN_NORMAL', 'CPU_AWAKE'] // array of allowed values
707  *   }
708  * ]
709  */
710 Validator.prototype.validateArgs = function(a, d) {
711   var args = {has: {}};
712
713   for (var i = 0; i < d.length; ++i) {
714     var name = d[i].name;
715     args.has[name] = (i < a.length);
716
717     var optional = d[i].optional;
718     var nullable = d[i].nullable;
719     var val = a[i];
720
721     if (args.has[name] || !optional) {
722       var type = d[i].type;
723       var values = d[i].values;
724
725       switch (type) {
726         case this.Types.BOOLEAN:
727           val = _converter.toBoolean(val, nullable);
728           break;
729
730         case this.Types.LONG:
731           val = _converter.toLong(val, nullable);
732           break;
733
734         case this.Types.LONG_LONG:
735           val = _converter.toLongLong(val, nullable);
736           break;
737
738         case this.Types.UNSIGNED_LONG:
739           val = _converter.toUnsignedLong(val, nullable);
740           break;
741
742         case this.Types.UNSIGNED_LONG_LONG:
743           val = _converter.toUnsignedLongLong(val, nullable);
744           break;
745
746         case this.Types.BYTE:
747           val = _converter.toByte(val, nullable);
748           break;
749
750         case this.Types.OCTET:
751           val = _converter.toOctet(val, nullable);
752           break;
753
754         case this.Types.DOUBLE:
755           val = _converter.toDouble(val, nullable);
756           break;
757
758         case this.Types.STRING:
759           val = _converter.toString(val, nullable);
760           break;
761
762         case this.Types.FUNCTION:
763           val = _converter.toFunction(val, nullable);
764           break;
765
766         case this.Types.DICTIONARY:
767           val = _converter.toDictionary(val, nullable);
768           break;
769
770         case this.Types.PLATFORM_OBJECT:
771           val = _converter.toPlatformObject(val, values, nullable);
772           break;
773
774         case this.Types.LISTENER:
775           if (_type.isNull(val)) {
776             if (!nullable) {
777               throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
778                   'Argument "' + name + '" cannot be null.');
779             }
780           } else {
781             if (!_type.isObject(val)) {
782               throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
783                   'Argument "' + name + '" should be an object.');
784             }
785             for (var ii = 0; ii < values.length; ++ii) {
786               if (_type.hasProperty(val, values[ii])) {
787                 val[values[ii]] = _converter.toFunction(val[values[ii]], false);
788               }
789             }
790           }
791           break;
792
793         case this.Types.ARRAY:
794           val = _converter.toArray(val, nullable);
795           if (!_type.isNull(val) && values) {
796             var func;
797
798             switch (values) {
799               case this.Types.BOOLEAN:
800                 func = _converter.toBoolean;
801                 break;
802
803               case this.Types.LONG:
804                 func = _converter.toLong;
805                 break;
806
807               case this.Types.LONG_LONG:
808                 func = _converter.toLongLong;
809                 break;
810
811               case this.Types.UNSIGNED_LONG:
812                 func = _converter.toUnsignedLong;
813                 break;
814
815               case this.Types.UNSIGNED_LONG_LONG:
816                 func = _converter.toUnsignedLongLong;
817                 break;
818
819               case this.Types.BYTE:
820                 func = _converter.toByte;
821                 break;
822
823               case this.Types.OCTET:
824                 func = _converter.toOctet;
825                 break;
826
827               case this.Types.DOUBLE:
828                 func = _converter.toDouble;
829                 break;
830
831               case this.Types.STRING:
832                 func = _converter.toString;
833                 break;
834
835               default:
836                 func = function(val) {
837                   if (!(val instanceof values)) {
838                     throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
839                         'Items of array "' + name + '" should be of type: ' + values + '.');
840                   }
841                   return val;
842                 };
843             }
844
845             for (var j = 0; j < val.length; ++j) {
846               val[j] = func(val[j]);
847             }
848           }
849           break;
850
851         case this.Types.ENUM:
852           val = _converter.toEnum(val, values, nullable);
853           break;
854
855         case this.Types.FILE_REFERENCE:
856           if (_type.isObject(val) && 'File' === val.constructor.name && val.fullPath) {
857             val = val.fullPath;
858           }
859           val = _converter.toString(val, nullable);
860           break;
861
862         case this.Types.SIMPLE_TYPE:
863           if (optional && _type.isUndefined(val)) {
864             break;
865           }
866           if (nullable && _type.isNull(val)) {
867             break;
868           }
869           if (!_type.isBoolean(val) && !_type.isNumber(val) && !_type.isString(val)) {
870             throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
871                 'Argument "' + name + '" should be boolean, number or string.');
872           }
873           break;
874
875         default:
876           throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
877               'Unknown type: "' + type + '".');
878       }
879
880       var _validator = d[i].validator;
881
882       if (_type.isFunction(_validator) && !_validator(val)) {
883         throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
884             'Argument "' + name + '" did not pass additional validation.');
885       }
886
887       args[name] = val;
888     }
889   }
890
891   return args;
892 };
893
894
895 /**
896  * @deprecated Use validateArgs() instead.
897  */
898 Validator.prototype.validateMethod = function(a, d) {
899   return this.validateArgs(a, d);
900 };
901
902
903 /**
904  * Use this helper to ensure that constructor is invoked by "new" operator.
905  *
906  * @param {Object} obj
907  * @param {Function} instance
908  */
909 Validator.prototype.isConstructorCall = function(obj, instance) {
910   if (!(obj instanceof instance) || obj._previouslyConstructed) {
911     // There is no TypeError exception in Tizen 2.3.0 API spec but it's required by current TCTs.
912     // For Tizen compliance it's wrapped into WebAPIException.
913     throw new WebAPIException('TypeError', 'Constructor cannot be called as function.');
914   }
915
916   Object.defineProperty(obj, '_previouslyConstructed', {
917     value: true,
918     writable: false,
919     enumerable: false
920   });
921 };
922
923
924 /**
925  * @deprecated Use isConstructorCall() instead.
926  */
927 Validator.prototype.validateConstructorCall = function(obj, instance) {
928   this.isConstructorCall(obj, instance);
929 };
930
931 var _validator = new Validator();
932
933
934
935 /////////////////////////////////////////////////////////////////////////////
936 /** @constructor */
937 var NativeManager = function(extension) {
938
939   /**
940    * @type {string}
941    * @const
942    */
943   this.CALLBACK_ID_KEY = 'callbackId';
944
945   /**
946    * @type {string}
947    * @const
948    */
949   this.LISTENER_ID_KEY = 'listenerId';
950
951   /**
952    * @type {Object}
953    * @private
954    */
955   var extension_ = extension;
956
957   /**
958    * @type {number}
959    * @private
960    */
961   var replyId_ = 0;
962
963   /**
964    * Map of async reply callbacks.
965    *
966    * @type {Object.<number, function>}
967    * @protected
968    */
969   this.callbacks_ = {};
970
971   /**
972    * Map of registered listeners.
973    *
974    * @type {Object.<string, function>}
975    * @protected
976    */
977   this.listeners_ = {};
978
979   _validator.isConstructorCall(this, NativeManager);
980
981   // TODO: Remove mockup if WRT implements sendRuntimeMessage
982   // This is temporary mockup!
983   extension.sendRuntimeMessage = extension.sendRuntimeMessage || function() {
984     xwalk.utils.error('Runtime did not implement extension.sendRuntimeMessage!');
985     throw new WebAPIException(WebAPIException.UNKNOWN_ERR,
986         'Runtime did not implement extension.sendRuntimeMessage!');
987   };
988
989   extension.sendRuntimeAsyncMessage = extension.sendRuntimeAsyncMessage || function() {
990     xwalk.utils.error('Runtime did not implement extension.sendRuntimeAsyncMessage!');
991     throw new WebAPIException(WebAPIException.UNKNOWN_ERR,
992         'Runtime did not implement extension.sendRuntimeAsyncMessage!');
993   };
994
995   extension.sendRuntimeSyncMessage = extension.sendRuntimeSyncMessage || function() {
996     xwalk.utils.error('Runtime did not implement extension.sendRuntimeSyncMessage!');
997     throw new WebAPIException(WebAPIException.UNKNOWN_ERR,
998         'Runtime did not implement extension.sendRuntimeSyncMessage!');
999   };
1000
1001   // check extension prototype
1002   if (!extension || !extension.internal ||
1003       !_type.isFunction(extension.postMessage) ||
1004       !_type.isFunction(extension.internal.sendSyncMessage) ||
1005       !_type.isFunction(extension.sendRuntimeMessage) ||
1006       !_type.isFunction(extension.sendRuntimeAsyncMessage) ||
1007       !_type.isFunction(extension.sendRuntimeSyncMessage) ||
1008       !_type.isFunction(extension.setMessageListener)) {
1009     throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
1010                               'Wrong extension object passed');
1011   }
1012
1013   Object.defineProperties(this, {
1014     nextReplyId: {
1015       get: function() {
1016         return ++replyId_;
1017       },
1018       enumerable: false
1019     },
1020     extension: {
1021       get: function() {
1022         return extension_;
1023       },
1024       enumerable: true
1025     }
1026   });
1027
1028   extension_.setMessageListener(function(json) {
1029     var msg = JSON_.parse(json);
1030     var id;
1031
1032     if (msg.hasOwnProperty(this.CALLBACK_ID_KEY)) {
1033       id = msg[this.CALLBACK_ID_KEY];
1034       delete msg[this.CALLBACK_ID_KEY];
1035
1036       if (!_type.isFunction(this.callbacks_[id])) {
1037         xwalk.utils.error('Wrong callback identifier. Ignoring message.');
1038         return;
1039       }
1040
1041       var f = this.callbacks_[id];
1042       setTimeout(function() {
1043         try {
1044           f(msg);
1045         } catch (e) {
1046           xwalk.utils.error('########## exception');
1047           xwalk.utils.error(e);
1048         }
1049       }, 0);
1050       delete this.callbacks_[id];
1051
1052       return;
1053     }
1054
1055     if (msg.hasOwnProperty(this.LISTENER_ID_KEY)) {
1056       id = msg[this.LISTENER_ID_KEY];
1057       delete msg[this.LISTENER_ID_KEY];
1058
1059       if (!_type.isFunction(this.listeners_[id])) {
1060         xwalk.utils.error('Wrong listener identifier. Ignoring message.');
1061         return;
1062       }
1063
1064       var f = this.listeners_[id];
1065       setTimeout(function() {
1066         try {
1067           f(msg);
1068         } catch (e) {
1069           xwalk.utils.error('########## exception');
1070           xwalk.utils.error(e);
1071         }
1072       }, 0);
1073
1074       return;
1075     }
1076
1077     xwalk.utils.error('Missing callback or listener identifier. Ignoring message.');
1078
1079   }.bind(this));
1080 };
1081
1082 NativeManager.prototype.call = function(cmd, args, callback) {
1083   args = args || {};
1084
1085   var replyId = this.nextReplyId;
1086   args[this.CALLBACK_ID_KEY] = replyId;
1087   this.callbacks_[replyId] = callback;
1088
1089   return this.callSync(cmd, args);
1090 };
1091
1092 NativeManager.prototype.callSync = function(cmd, args) {
1093   var request = JSON_.stringify({
1094     cmd: cmd,
1095     args: args || {}
1096   });
1097
1098   var response = this.extension.internal.sendSyncMessage(request);
1099   if( response === undefined ) {
1100     /* C++ extension didn't set sync response using Instance::SendSyncReply */
1101     throw new WebAPIException(WebAPIException.ABORT_ERR, "Internal error");
1102   }
1103   return JSON_.parse(response);
1104 };
1105
1106 NativeManager.prototype.sendRuntimeMessage = function(msg, body) {
1107   return this.extension.sendRuntimeMessage(msg, body || '');
1108 };
1109
1110 NativeManager.prototype.sendRuntimeAsyncMessage = function(msg, body, callback) {
1111   var handler = function(response) {
1112     if (_type.isFunction(callback)) {
1113       var result = {};
1114       if ('success' === response.toLowerCase()) {
1115         result.status = 'success';
1116       } else {
1117         result.status = 'error';
1118         result.error = new WebAPIException(WebAPIException.UNKNOWN_ERR,
1119                                            'Runtime message failure');
1120       }
1121       callback(result);
1122     }
1123   };
1124   return this.extension.sendRuntimeAsyncMessage(msg, body || '', handler);
1125 };
1126
1127 NativeManager.prototype.sendRuntimeSyncMessage = function(msg, body) {
1128   return this.extension.sendRuntimeSyncMessage(msg, body || '');
1129 };
1130
1131 NativeManager.prototype.addListener = function(name, callback) {
1132   if (!_type.isString(name) || !name.length) {
1133     throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR);
1134   }
1135
1136   this.listeners_[name] = callback;
1137 };
1138
1139 NativeManager.prototype.removeListener = function(name) {
1140   if (this.listeners_.hasOwnProperty(name)) {
1141     delete this.listeners_[name];
1142   }
1143 };
1144
1145 NativeManager.prototype.isListenerSet = function(name) {
1146   return this.listeners_.hasOwnProperty(name);
1147 };
1148
1149 NativeManager.prototype.isSuccess = function(result) {
1150   return (result.status !== 'error');
1151 };
1152
1153 NativeManager.prototype.isFailure = function(result) {
1154   return !this.isSuccess(result);
1155 };
1156
1157 NativeManager.prototype.getResultObject = function(result) {
1158   return result.result;
1159 };
1160
1161 NativeManager.prototype.getErrorObject = function(result) {
1162   return new WebAPIException(result.error);
1163 };
1164
1165 NativeManager.prototype.callIfPossible = function(callback) {
1166   if (!_type.isNullOrUndefined(callback)) {
1167     callback.apply(callback, [].slice.call(arguments, 1));
1168   }
1169 };
1170
1171 // WebAPIException and WebAPIError definition moved to Utils for compliance
1172 // reasons with blink-wrt environment.
1173 // In blink-wrt the original Tizen module is loaded, which is not providing exception constructor.
1174 // As modules needs exceptions internally so they are loaded here for now.
1175 // See http://168.219.209.56/gerrit/#/c/23472/ for more details.
1176 // In future exception definition could be moved back to Tizen module.
1177 function __isObject(object) {
1178   return object instanceof _global.Object;
1179 }
1180
1181 function __isUndefined(object) {
1182   return object === void 0;
1183 }
1184
1185 function __isNumber(object) {
1186   return typeof object === 'number';
1187 }
1188
1189 // WARNING! This list should be in sync with the equivalent enum
1190 // located at tizen.h. Remember to update tizen.h if you change
1191 // something here.
1192 var errors = {
1193   NO_ERROR: 0,
1194   UNKNOWN_ERR: -1,
1195
1196   INDEX_SIZE_ERR: 1,
1197   DOMSTRING_SIZE_ERR: 2,
1198   HIERARCHY_REQUEST_ERR: 3,
1199   WRONG_DOCUMENT_ERR: 4,
1200   INVALID_CHARACTER_ERR: 5,
1201   NO_DATA_ALLOWED_ERR: 6,
1202   NO_MODIFICATION_ALLOWED_ERR: 7,
1203   NOT_FOUND_ERR: 8,
1204   NOT_SUPPORTED_ERR: 9,
1205   INUSE_ATTRIBUTE_ERR: 10,
1206   INVALID_STATE_ERR: 11,
1207   SYNTAX_ERR: 12,
1208   INVALID_MODIFICATION_ERR: 13,
1209   NAMESPACE_ERR: 14,
1210   INVALID_ACCESS_ERR: 15,
1211   VALIDATION_ERR: 16,
1212   TYPE_MISMATCH_ERR: 17,
1213   SECURITY_ERR: 18,
1214   NETWORK_ERR: 19,
1215   ABORT_ERR: 20,
1216   URL_MISMATCH_ERR: 21,
1217   QUOTA_EXCEEDED_ERR: 22,
1218   TIMEOUT_ERR: 23,
1219   INVALID_NODE_TYPE_ERR: 24,
1220   DATA_CLONE_ERR: 25,
1221
1222   // Error codes for these errors are not really defined anywhere.
1223   INVALID_VALUES_ERR: 100,
1224   IO_ERR: 101,
1225   PERMISSION_DENIED_ERR: 102,
1226   SERVICE_NOT_AVAILABLE_ERR: 103,
1227   DATABASE_ERR: 104,
1228   VERIFICATION_ERR: 105
1229 };
1230
1231 var code_to_name = {};
1232 code_to_name[errors['NO_ERROR']] = 'NoError';
1233 code_to_name[errors['UNKNOWN_ERR']] = 'UnknownError';
1234 code_to_name[errors['INDEX_SIZE_ERR']] = 'IndexSizeError';
1235 code_to_name[errors['DOMSTRING_SIZE_ERR']] = 'DOMStringSizeError';
1236 code_to_name[errors['HIERARCHY_REQUEST_ERR']] = 'HierarchyRequestError';
1237 code_to_name[errors['WRONG_DOCUMENT_ERR']] = 'WrongDocumentError';
1238 code_to_name[errors['INVALID_CHARACTER_ERR']] = 'InvalidCharacterError';
1239 code_to_name[errors['NO_DATA_ALLOWED_ERR']] = 'NoDataAllowedError';
1240 code_to_name[errors['NO_MODIFICATION_ALLOWED_ERR']] = 'NoModificationAllowedError';
1241 code_to_name[errors['NOT_FOUND_ERR']] = 'NotFoundError';
1242 code_to_name[errors['NOT_SUPPORTED_ERR']] = 'NotSupportedError';
1243 code_to_name[errors['INUSE_ATTRIBUTE_ERR']] = 'InuseAttributeError';
1244 code_to_name[errors['INVALID_STATE_ERR']] = 'InvalidStateError';
1245 code_to_name[errors['SYNTAX_ERR']] = 'SyntaxError';
1246 code_to_name[errors['INVALID_MODIFICATION_ERR']] = 'InvalidModificationError';
1247 code_to_name[errors['NAMESPACE_ERR']] = 'NamespaceError';
1248 code_to_name[errors['INVALID_ACCESS_ERR']] = 'InvalidAccessError';
1249 code_to_name[errors['VALIDATION_ERR']] = 'ValidationError';
1250 code_to_name[errors['TYPE_MISMATCH_ERR']] = 'TypeMismatchError';
1251 code_to_name[errors['SECURITY_ERR']] = 'SecurityError';
1252 code_to_name[errors['NETWORK_ERR']] = 'NetworkError';
1253 code_to_name[errors['ABORT_ERR']] = 'AbortError';
1254 code_to_name[errors['URL_MISMATCH_ERR']] = 'URLMismatchError';
1255 code_to_name[errors['QUOTA_EXCEEDED_ERR']] = 'QuotaExceededError';
1256 code_to_name[errors['TIMEOUT_ERR']] = 'TimeoutError';
1257 code_to_name[errors['INVALID_NODE_TYPE_ERR']] = 'InvalidNodeTypeError';
1258 code_to_name[errors['DATA_CLONE_ERR']] = 'DataCloneError';
1259
1260 code_to_name[errors['INVALID_VALUES_ERR']] = 'InvalidValuesError';
1261 code_to_name[errors['IO_ERR']] = 'IOError';
1262 code_to_name[errors['PERMISSION_DENIED_ERR']] = 'PermissionDeniedError';
1263 code_to_name[errors['SERVICE_NOT_AVAILABLE_ERR']] = 'ServiceNotAvailableError';
1264 code_to_name[errors['DATABASE_ERR']] = 'DatabaseError';
1265 code_to_name[errors['VERIFICATION_ERR']] = 'VerificationError';
1266
1267 var name_to_code = {};
1268 Object.keys(errors).forEach(function(key) {
1269   name_to_code[code_to_name[errors[key]]] = errors[key];
1270 });
1271
1272
1273 /**
1274  * Generic exception interface.
1275  *
1276  * @param {number} code 16-bit error code.
1277  * @param {string} message An error message that describes the details of an encountered error.
1278  * @param {string} name An error type.
1279  */
1280 var WebAPIException = function(code, message, name) {
1281   var code_ = 0;
1282   var name_ = code_to_name[code];
1283   var message_ = 'Unknown error';
1284
1285   switch (arguments.length) {
1286     case 1:
1287       var error = arguments[0];
1288       if (__isObject(error)) {
1289         code_ = error.code;
1290         name_ = error.name;
1291         message_ = error.message;
1292         if (__isUndefined(code_) && !__isUndefined(name_))
1293           code_ = name_to_code[name_];
1294         if (__isUndefined(name_) && !__isUndefined(code_))
1295           name_ = code_to_name[code_];
1296       } else if (__isNumber(error)) {
1297         // backward compatibility with crosswalk implementation
1298         code_ = error;
1299         name_ = code_to_name[code];
1300         message_ = name_;
1301       }
1302       break;
1303     case 2:
1304       if (__isNumber(arguments[0])) {
1305         code_ = arguments[0];
1306         if (!__isUndefined(code_to_name[code_])) {
1307           name_ = code_to_name[code_];
1308         }
1309       } else {
1310         name_ = String(arguments[0]);
1311         if (!__isUndefined(name_to_code[name_])) {
1312           code_ = name_to_code[name_];
1313         }
1314       }
1315       message_ = String(arguments[1]);
1316       break;
1317     case 3:
1318       // backward compatibility with crosswalk implementation
1319       code_ = Number(arguments[0]);
1320       message_ = String(arguments[1]);
1321       name_ = String(arguments[2]);
1322       break;
1323     default:
1324       return;
1325   }
1326
1327   if (code_ > errors.DATA_CLONE_ERR) {
1328     code_ = 0;
1329   }
1330
1331   // attributes
1332   Object.defineProperties(this, {
1333     code: {value: code_, writable: false, enumerable: true},
1334     name: {value: name_, writable: false, enumerable: true},
1335     message: {value: message_, writable: false, enumerable: true}
1336   });
1337
1338   this.constructor.prototype.__proto__ = Error.prototype;
1339   Error.captureStackTrace && Error.captureStackTrace(this, this.constructor); // V8-specific code
1340 };
1341
1342 WebAPIException.prototype.toString = function() {
1343   return this.name + ': ' + this.message;
1344 };
1345
1346
1347 var error_constants = {};
1348 for (var prop in errors) {
1349   error_constants[prop] = {value: errors[prop], writable: false, enumerable: true};
1350 }
1351 Object.defineProperties(WebAPIException, error_constants);
1352 Object.defineProperties(WebAPIException.prototype, error_constants);
1353
1354
1355 // Export WebAPIException and WebAPIError into global scope.
1356 // For compliance reasons their constructors should not be exported in tizen namespace,
1357 // but should be available internally to allow throwing exceptions from modules.
1358 var scope;
1359 if (typeof window !== 'undefined') {
1360   scope = window;
1361 } else if(typeof global !== 'undefined') {
1362   scope = global;
1363 }
1364 scope = scope || {};
1365 scope.WebAPIException = WebAPIException;
1366 scope.WebAPIError = WebAPIException;
1367
1368 Utils.prototype.dateConverter = _dateConverter;
1369 Utils.prototype.type = _type;
1370 Utils.prototype.converter = _converter;
1371 Utils.prototype.validator = _validator;
1372 Utils.prototype.NativeManager = NativeManager;
1373
1374 var native_ = new NativeManager(extension);
1375
1376 exports.utils = new Utils();
1377
1378 Object.freeze(exports);
1379 Object.freeze(exports.utils);
1380 Object.freeze(Utils.prototype);
1381 Object.freeze(NativeManager.prototype);
1382