2 * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 import './exception_handling';
19 interface NativeXWalkExtension extends NativeWRTjs.XWalkExtension {
23 let instance: XWalkExtension | undefined;
24 let api_: { [key: string]: any } = {};
25 let extensions_: { [key: string]: NativeXWalkExtension } = {};
27 class XWalkExtension {
29 const binding: NativeWRTjs.XWalkExtensionBinding = process.wrtBinding('wrt_xwalk_extension')
30 var extensions: NativeXWalkExtension[] = binding.getExtensions();
31 for (var i = 0; i < extensions.length; i++) {
32 extensions[i].loaded = false;
33 console.log("Load extension : " + extensions[i].name);
34 extensions_[extensions[i].name] = extensions[i];
36 for (var name in extensions_) {
37 if (!extensions_[name].use_trampoline) {
38 this.load(extensions_[name]);
41 for (var name in extensions_) {
42 if (extensions_[name].use_trampoline) {
43 this.installTrampoline(extensions_[name]);
49 * Creates namespace for 'name' in given object.
50 * Eg. this.createNamespace(global, 'tizen.contact') will create:
51 * global.tizen.contact = {}
53 * @param {Object} object
54 * @param {String} name
56 createNamespace(object: { [key: string]: any }, name: string) {
58 var arr = name.split('.');
59 for (var i = 0; i < arr.length; i++) {
60 obj[arr[i]] = obj[arr[i]] || {};
65 exposeApi(ext: NativeXWalkExtension) {
66 var i, entry_points, entry_point, tmp, parent_name, base_name;
68 // additional entry points are installed in global context by eval()
69 // so we need to move it to protected api_ object first
70 entry_points = [...new Set(ext.entry_points)];
71 for (i = 0; i < entry_points.length; i++) {
72 entry_point = entry_points[i];
73 tmp = entry_point.split('.');
75 base_name = tmp[tmp.length - 1];
77 api_[parent_name][base_name] = global[parent_name][base_name];
78 delete global[parent_name][base_name];
81 entry_points.push(ext.name);
83 for (i = 0; i < entry_points.length; i++) {
84 entry_point = entry_points[i];
85 tmp = entry_point.split('.');
87 base_name = tmp[tmp.length - 1];
89 Object.defineProperty(global[parent_name], base_name, {
90 get: function (parent_name, base_name) {
92 return api_[parent_name][base_name];
94 }(parent_name, base_name),
101 static runtimeMessageHandler(type: string, data?: string, callback?: (message: string) => void): void {
102 console.log('This is prototype of runtimeMessageHandler');
106 * @param {Object} ext
108 load(ext: NativeXWalkExtension) {
115 this.createNamespace(api_, ext.name);
116 this.createNamespace(global, ext.name);
118 var api = (ext.use_trampoline) ? api_ : global;
121 '(function(extension) {' +
122 ' extension.internal = {};' +
123 ' extension.internal.sendSyncMessage = extension.sendSyncMessage;' +
124 ' delete extension.sendSyncMessage;' +
125 ' var exports = {}; ' +
126 ' (function() {\'use strict\'; ' + ext.jsapi + '})();' +
127 ' api.' + ext.name + ' = exports; ' +
131 var func = eval(jscode);
133 postMessage: function(msg: string) {
134 return ext.postMessage(msg);
136 sendSyncMessage: function(msg: string) {
137 return ext.sendSyncMessage(msg);
139 setMessageListener: function(fn: (message: string) => void) {
140 return ext.setMessageListener(fn);
142 sendRuntimeMessage: function(type: string, data?: string) {
143 return XWalkExtension.runtimeMessageHandler(type, data);
145 sendRuntimeSyncMessage: function(type: string, data?: string) {
146 return XWalkExtension.runtimeMessageHandler(type, data);
148 sendRuntimeAsyncMessage: function(type: string, data?: string, callback?: (message: string) => void) {
149 return XWalkExtension.runtimeMessageHandler(type, data, callback);
153 if (ext.use_trampoline) {
157 console.log('Error loading extension "' + ext.name + '": ' + err.message);
162 * This is used to defer extension loading to it's first usage.
163 * Eg. First access to tizen.contact will load extension's 'jsapi' through eval().
165 * @param {Object} ext
167 installTrampoline(ext: NativeXWalkExtension) {
168 var entry_points = [...new Set(ext.entry_points)];
169 entry_points.push(ext.name);
170 for (var i = 0; i < entry_points.length; i++) {
171 var tmp = entry_points[i].split('.');
172 var parent_name = tmp[0];
173 var base_name = tmp[tmp.length - 1];
175 this.createNamespace(global, entry_points[i]);
177 Object.defineProperty(global[parent_name], base_name, {
178 get: function (this: XWalkExtension, parent_name: string, base_name: string) {
179 return function(this: XWalkExtension) {
181 this.deleteTrampoline(ext);
183 return api_[parent_name][base_name];
185 console.log(e.stack);
188 }.call(this, parent_name, base_name),
194 deleteTrampoline(ext: NativeXWalkExtension) {
195 var entry_points = [...new Set(ext.entry_points)];
196 entry_points.push(ext.name);
198 for (var i = 0; i < entry_points.length; i++) {
199 var tmp = entry_points[i].split('.');
200 var parent_name = tmp[0];
201 var base_name = tmp[tmp.length - 1];
202 delete global[parent_name][base_name];
207 export const initialize = () => {
209 instance = new XWalkExtension;
212 export const setRuntimeMessageHandler = (handler: (type: string, data?: string, callback?: (message: string) => void) => void) => {
213 XWalkExtension.runtimeMessageHandler = handler;
216 export let cleanup = () => {
217 for (var name in extensions_) {
218 extensions_[name].unloadInstance();
221 instance = undefined;
224 export const preventCleanup = () => {