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 } = {};
26 global.window = global.window ?? global;
28 class XWalkExtension {
30 const binding: NativeWRTjs.XWalkExtensionBinding = process.wrtBinding('wrt_xwalk_extension')
31 var extensions: NativeXWalkExtension[] = binding.getExtensions();
32 for (var i = 0; i < extensions.length; i++) {
33 extensions[i].loaded = false;
34 console.log("Load extension : " + extensions[i].name);
35 extensions_[extensions[i].name] = extensions[i];
37 for (var name in extensions_) {
38 if (!extensions_[name].use_trampoline) {
39 this.load(extensions_[name]);
42 for (var name in extensions_) {
43 if (extensions_[name].use_trampoline) {
44 this.installTrampoline(extensions_[name]);
50 * Creates namespace for 'name' in given object.
51 * Eg. this.createNamespace(global, 'tizen.contact') will create:
52 * global.tizen.contact = {}
54 * @param {Object} object
55 * @param {String} name
57 createNamespace(object: { [key: string]: any }, name: string) {
59 var arr = name.split('.');
60 for (var i = 0; i < arr.length; i++) {
61 obj[arr[i]] = obj[arr[i]] || {};
66 exposeApi(ext: NativeXWalkExtension) {
67 var i, entry_points, entry_point, tmp, parent_name, base_name;
69 // additional entry points are installed in global context by eval()
70 // so we need to move it to protected api_ object first
71 entry_points = [...new Set(ext.entry_points)];
72 for (i = 0; i < entry_points.length; i++) {
73 entry_point = entry_points[i];
74 tmp = entry_point.split('.');
76 base_name = tmp[tmp.length - 1];
78 api_[parent_name][base_name] = global[parent_name][base_name];
79 delete global[parent_name][base_name];
82 entry_points.push(ext.name);
84 for (i = 0; i < entry_points.length; i++) {
85 entry_point = entry_points[i];
86 tmp = entry_point.split('.');
88 base_name = tmp[tmp.length - 1];
90 Object.defineProperty(global[parent_name], base_name, {
91 get: function (parent_name, base_name) {
93 return api_[parent_name][base_name];
95 }(parent_name, base_name),
102 static runtimeMessageHandler(type: string, data?: string, callback?: (message: string) => void): void {
103 console.log('This is prototype of runtimeMessageHandler');
107 * @param {Object} ext
109 load(ext: NativeXWalkExtension) {
116 this.createNamespace(api_, ext.name);
117 this.createNamespace(global, ext.name);
119 var api = (ext.use_trampoline) ? api_ : global;
120 var extension_api = ext.jsapi;
121 if (global.serviceType === 'GLOBAL' && ext.name === 'xwalk') {
122 console.log(`Delete freeze exports.utils for override method`);
123 extension_api = extension_api.replace('Object.freeze(exports.utils);', '');
124 extension_api = extension_api.replace('Object.freeze(Utils.prototype);', '');
127 '(function(extension) {' +
128 ' extension.internal = {};' +
129 ' extension.internal.sendSyncMessage = extension.sendSyncMessage;' +
130 ' delete extension.sendSyncMessage;' +
131 ' var exports = {}; ' +
132 ' (function() {\'use strict\'; ' + extension_api + '})();' +
133 ' api.' + ext.name + ' = exports; ' +
137 var func = eval(jscode);
139 postMessage: function(msg: string) {
140 return ext.postMessage(msg);
142 sendSyncMessage: function(msg: string) {
143 return ext.sendSyncMessage(msg);
145 setMessageListener: function(fn: (message: string) => void) {
146 return ext.setMessageListener(fn);
148 sendRuntimeMessage: function(type: string, data?: string) {
149 return XWalkExtension.runtimeMessageHandler(type, data);
151 sendRuntimeSyncMessage: function(type: string, data?: string) {
152 return XWalkExtension.runtimeMessageHandler(type, data);
154 sendRuntimeAsyncMessage: function(type: string, data?: string, callback?: (message: string) => void) {
155 return XWalkExtension.runtimeMessageHandler(type, data, callback);
159 if (ext.use_trampoline) {
163 console.log('Error loading extension "' + ext.name + '": ' + err.message);
168 * This is used to defer extension loading to it's first usage.
169 * Eg. First access to tizen.contact will load extension's 'jsapi' through eval().
171 * @param {Object} ext
173 installTrampoline(ext: NativeXWalkExtension) {
174 var entry_points = [...new Set(ext.entry_points)];
175 entry_points.push(ext.name);
176 for (var i = 0; i < entry_points.length; i++) {
177 var tmp = entry_points[i].split('.');
178 var parent_name = tmp[0];
179 var base_name = tmp[tmp.length - 1];
181 this.createNamespace(global, entry_points[i]);
183 Object.defineProperty(global[parent_name], base_name, {
184 get: function (this: XWalkExtension, parent_name: string, base_name: string) {
185 return function(this: XWalkExtension) {
187 this.deleteTrampoline(ext);
189 return api_[parent_name][base_name];
191 console.log(e.stack);
194 }.call(this, parent_name, base_name),
200 deleteTrampoline(ext: NativeXWalkExtension) {
201 var entry_points = [...new Set(ext.entry_points)];
202 entry_points.push(ext.name);
204 for (var i = 0; i < entry_points.length; i++) {
205 var tmp = entry_points[i].split('.');
206 var parent_name = tmp[0];
207 var base_name = tmp[tmp.length - 1];
208 delete global[parent_name][base_name];
213 export const initialize = () => {
215 instance = new XWalkExtension;
218 export const setRuntimeMessageHandler = (handler: (type: string, data?: string, callback?: (message: string) => void) => void) => {
219 XWalkExtension.runtimeMessageHandler = handler;
222 export let cleanup = () => {
224 instance = undefined;
227 export const preventCleanup = () => {