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 ' extension.internal.sendSyncMessageWithStringReply = extension.sendSyncMessageWithStringReply;' +
131 ' extension.internal.sendSyncMessageWithBinaryReply = extension.sendSyncMessageWithBinaryReply;' +
132 ' delete extension.sendSyncMessage;' +
133 ' delete extension.sendSyncMessageWithStringReply;' +
134 ' delete extension.sendSyncMessageWithBinaryReply;' +
135 ' var exports = {}; ' +
136 ' (function() {\'use strict\'; ' + extension_api + '})();' +
137 ' api.' + ext.name + ' = exports; ' +
141 var func = eval(jscode);
143 postMessage: function(msg: string) {
144 return ext.postMessage(msg);
146 sendSyncMessage: function(msg: string) {
147 return ext.sendSyncMessage(msg);
149 sendSyncMessageWithStringReply: function(msg: string) {
150 return ext.sendSyncMessageWithStringReply(msg);
152 sendSyncMessageWithBinaryReply: function(msg: string) {
153 return ext.sendSyncMessageWithBinaryReply(msg);
155 setMessageListener: function(fn: (message: string) => void) {
156 return ext.setMessageListener(fn);
158 sendRuntimeMessage: function(type: string, data?: string) {
159 return XWalkExtension.runtimeMessageHandler(type, data);
161 sendRuntimeSyncMessage: function(type: string, data?: string) {
162 return XWalkExtension.runtimeMessageHandler(type, data);
164 sendRuntimeAsyncMessage: function(type: string, data?: string, callback?: (message: string) => void) {
165 return XWalkExtension.runtimeMessageHandler(type, data, callback);
169 if (ext.use_trampoline) {
173 console.log('Error loading extension "' + ext.name + '": ' + err.message);
178 * This is used to defer extension loading to it's first usage.
179 * Eg. First access to tizen.contact will load extension's 'jsapi' through eval().
181 * @param {Object} ext
183 installTrampoline(ext: NativeXWalkExtension) {
184 var entry_points = [...new Set(ext.entry_points)];
185 entry_points.push(ext.name);
186 for (var i = 0; i < entry_points.length; i++) {
187 var tmp = entry_points[i].split('.');
188 var parent_name = tmp[0];
189 var base_name = tmp[tmp.length - 1];
191 this.createNamespace(global, entry_points[i]);
193 Object.defineProperty(global[parent_name], base_name, {
194 get: function (this: XWalkExtension, parent_name: string, base_name: string) {
195 return function(this: XWalkExtension) {
197 this.deleteTrampoline(ext);
199 return api_[parent_name][base_name];
201 console.log(e.stack);
204 }.call(this, parent_name, base_name),
210 deleteTrampoline(ext: NativeXWalkExtension) {
211 var entry_points = [...new Set(ext.entry_points)];
212 entry_points.push(ext.name);
214 for (var i = 0; i < entry_points.length; i++) {
215 var tmp = entry_points[i].split('.');
216 var parent_name = tmp[0];
217 var base_name = tmp[tmp.length - 1];
218 delete global[parent_name][base_name];
223 export const initialize = () => {
225 instance = new XWalkExtension;
228 export const setRuntimeMessageHandler = (handler: (type: string, data?: string, callback?: (message: string) => void) => void) => {
229 XWalkExtension.runtimeMessageHandler = handler;
232 export let cleanup = () => {
234 instance = undefined;
237 export const preventCleanup = () => {