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.
19 const wrt = require('../browser/wrt'); // Load first for log
20 const AddonManager = require('./addon_manager');
21 const {app, ipcMain} = require('electron');
22 const IPC_MESSAGE = require('./ipc_message');
23 const TimerManager = require('../service/timer_manager');
24 const TizenExtension = require('../service/tizen_extension');
25 const WAS_EVENT = require('./was_event');
26 const WebApplication = require('./web_application');
29 constructor(options) {
30 this.webApplication = null;
31 this.handleIpcMessages();
32 this.addonManager = null;
33 this.isLaunched = false;
34 this.inspectorEnabledByVconf = false;
36 this.webContents = null;
38 this.isTVProfile = (wrt.getPlatformType() === 'product_tv');
41 app.on('before-quit', function(event) {
42 console.log('before-quit');
43 if (!wrt.isElectronApp()) {
44 _this.webApplication.quit();
45 _this.webApplication.finalize();
46 _this.webApplication = null;
49 app.on('will-quit', function(event) {
50 console.log('will-quit');
51 _this.addonManager.deactivateAll(app);
53 app.on('quit', function(event) {
57 app.on('browser-window-blur', function() {
58 console.log('browser-window-blur');
60 app.on('browser-window-focus', function() {
61 console.log('browser-window-focus');
63 app.on('browser-window-created', function() {
64 console.log('browser-window-created');
65 if (!_this.isLaunched) {
66 _this.addonManager.activateAll(app);
67 _this.isLaunched = true;
70 app.on('gpu-process-crashed', function() {
71 console.error('gpu-process-crashed');
73 app.on('window-all-closed', function(event) {
74 console.log('window-all-closed');
77 app.on('will-finish-launching', function(event) {
78 console.log('will-finish-launching');
80 app.on('web-contents-created', function(event, webContents) {
81 console.log('web-contents-created');
82 _this.webContents = webContents;
83 _this.webContents.on('before-input-event', function(event, input) {
84 if (_this.isLaunched && _this.webApplication) {
85 _this.handleKeyEvents(input.key);
89 app.once('ready', function(event) {
91 _this.addonManager = new AddonManager();
92 if (!options.noAddons) {
93 _this.addonManager.build();
95 wrt.importCertificate('');
97 wrt.on('app-control', function(event, appControl) {
98 console.log('app-control');
99 let loadInfo = appControl.getLoadInfo();
100 let src = loadInfo.getSrc();
102 if (wrt.isElectronApp()) {
103 console.log('Electron App launch');
104 const Module = require('module');
105 Module.globalPaths.push(wrt.getAppPath());
106 let filePath = src[7] === '/' ? src.substr(8) : src.substr(7); // strip "file://"
107 let pkgJson = require(filePath);
108 let pos = filePath.lastIndexOf('/');
110 let mainJsPath = (pos !== -1 ? filePath.substr(0, pos + 1) : '') +
111 (pkgJson.main || 'index.js');
112 console.log('loading path:', mainJsPath);
113 Module._load(mainJsPath, Module, true);
116 console.log('Tizen Web App launch');
117 let launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode');
118 if (!_this.webApplication) {
119 console.log('Creating WebApplication');
120 options.isAddonAvailable = !options.noAddons &&
121 _this.addonManager.isAddonAvailable();
122 options.launchMode = launchMode;
123 _this.webApplication = new WebApplication(options);
124 _this.webApplication.addonEmitter =
125 _this.addonManager.evt_emitter_;
126 _this.inspectorEnabledByVconf = wrt.needUseInspector();
127 if (_this.inspectorEnabledByVconf && launchMode != 'backgroundExecution') {
128 _this.webApplication.inspectorSrc = src;
131 _this.webApplication.mainWindow.loadURL(src);
132 _this.webApplication.prelaunch(src);
134 console.log('Handling app-control event');
135 if (_this.webApplication.preloadStatus == 'readyToShow') {
136 _this.webApplication.show();
138 if (launchMode != 'backgroundAtStartup') {
139 _this.webApplication.preloadStatus = 'none';
143 let skipReload = appControl.getData('SkipReload');
144 if (skipReload == 'Yes') {
145 console.log('skipping reload');
146 // TODO : Need to care this situation and decide to pass the addon event emitter to resume()
147 _this.webApplication.resume();
151 let reload = loadInfo.getReload() || _this.webApplication.isAlwaysReload;
153 if (_this.isTVProfile) {
154 console.log(`src = ${src}, app-control uri = ${_this.webApplication.mainWindow.getURL()}`);
155 const url = require('url');
156 let appcontrolUrl = url.parse(src);
157 let originUrl = url.parse(_this.webApplication.mainWindow.getURL());
158 if (appcontrolUrl.protocol !== originUrl.protocol ||
159 appcontrolUrl.host !== originUrl.host ||
160 appcontrolUrl.pathname !== originUrl.pathname) {
164 if (src != _this.webApplication.mainWindow.getURL()) {
169 // handle http://tizen.org/appcontrol/operation/main operation specially.
170 // only menu-screen app can send launch request with main operation.
171 // in this case, web app should have to resume web app not reset.
172 if (reload && appControl.getOperation() == 'http://tizen.org/appcontrol/operation/main')
175 _this.webApplication.closeWindows();
176 _this.webApplication.mainWindow.loadURL(src);
178 _this.webApplication.sendAppControlEvent();
182 _this.configureRuntime(appControl);
184 wrt.on('suspend', function() {
185 console.log('suspend');
186 if (_this.webApplication)
187 _this.webApplication.suspend();
189 wrt.on('resume', function() {
190 console.log('resume');
191 if (_this.webApplication)
192 _this.webApplication.resume();
194 wrt.on('low-memory', function() {
195 console.log('low-memory');
196 if (_this.webApplication)
197 _this.webApplication.lowMemory();
199 wrt.on('message', function(event, type, params) {
200 console.log('message type(' + type + ') params : ' + params);
201 const app_id = params[0];
202 const vm = require('vm');
203 if (type === 'startService') {
204 if (_this.sandbox[app_id] === undefined) {
205 const fs = require('fs');
206 const Module = require('module');
207 _this.sandbox[app_id] = {
213 for(let key in global) {
214 _this.sandbox[app_id][key] = global[key];
216 _this.sandbox[app_id]['timer_manager'] = new TimerManager();
217 const timer_api = _this.sandbox[app_id]['timer_manager'].getTimerAPI();
218 for(let key in timer_api) {
219 _this.sandbox[app_id][key] = timer_api[key];
221 let standard_object_list = [ Error, EvalError, RangeError, ReferenceError,
222 SyntaxError, TypeError, URIError, Number, BigInt, Math, Date,
223 String, RegExp, Array, Int8Array, Uint8Array, Uint8ClampedArray,
224 Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array,
225 Float64Array, BigInt64Array, BigUint64Array, Map, Set, WeakMap,
226 WeakSet, ArrayBuffer, DataView, JSON, Promise, Reflect, Proxy,
227 Intl, Intl.Collator, Intl.DateTimeFormat, Intl.NumberFormat, Intl.PluralRules,
228 WebAssembly, WebAssembly.Module, WebAssembly.Instance, WebAssembly.Memory,
229 WebAssembly.Table, WebAssembly.CompileError, WebAssembly.LinkError,
230 WebAssembly.RuntimeError, Boolean, Function, Object, Symbol ];
231 for (let idx in standard_object_list) {
232 _this.sandbox[app_id][standard_object_list[idx].name] = standard_object_list[idx];
235 let code = fs.readFileSync(params[1]);
236 vm.runInNewContext(code, _this.sandbox[app_id], options);
238 if (_this.sandbox[app_id]['started'] === undefined) {
239 _this.sandbox[app_id]['started'] = true;
240 _this.sandbox[app_id]['stopped'] = undefined;
241 const start_callback_string = 'if (module.exports.onStart !== undefined) { module.exports.onStart(); }';
242 vm.runInContext(start_callback_string, _this.sandbox[app_id]);
244 console.log('UI service has been started.');
246 event.preventDefault();
247 } else if (type === 'stopService') {
248 if (_this.sandbox[app_id]['stopped'] === undefined) {
249 _this.sandbox[app_id]['stopped'] = true;
250 _this.sandbox[app_id]['started'] = undefined;
251 const stop_callback_string = 'if (module.exports.onStop !== undefined) { module.exports.onStop(); }';
252 vm.runInContext(stop_callback_string, _this.sandbox[app_id]);
253 _this.sandbox[app_id]['timer_manager'].releaseRemainingTimers();
254 _this.sandbox[app_id] = undefined;
256 console.log('UI service has been stopped.');
258 event.preventDefault();
261 wrt.on('addon-installed', function(event, path) {
262 console.log('addon-installed at ' + path);
263 let dbInfo = AddonManager.checkAddon(path);
265 _this.addonPkgs.push(dbInfo);
268 wrt.on('addon-uninstalled', function(event, id) {
269 console.log('addon-unistalled named ' + id);
271 wrt.on('wgt-checking-done', function(event) {
272 console.log('wgt-checking-done');
273 AddonManager.updateDB(_this.addonPkgs);
275 /* FIXME: will uncheck after chromium-efl released */
276 if (wrt.getPlatformType() !== "product_tv") {
277 wrt.getInstalledPkg();
280 handleIpcMessages() {
282 ipcMain.on(IPC_MESSAGE.ADDONS.INSTALLED, (sender, name) => {
283 console.log('handleIpcMessages: INSTALLED ' + name);
284 _this.addonManager.build();
285 return _this.addonManager.activate(app, name);
287 ipcMain.on(IPC_MESSAGE.ADDONS.UNINSTALLED, (sender, name, pkg) => {
288 console.log('handleIpcMessages: UNINSTALLED ' + name);
289 _this.addonManager.deactivate(app, name);
290 /* FIXME: will uncheck after chromium-efl released */
291 if (wrt.getPlatformType() !== "product_tv") {
292 wrt.reqUninstallPkg(pkg);
296 ipcMain.on(IPC_MESSAGE.ADDONS.ACTIVATE, (sender, name) => {
297 console.log('handleIpcMessages: ACTIVATE ' + name);
298 return _this.addonManager.activate(app, name);
300 ipcMain.on(IPC_MESSAGE.ADDONS.DEACTIVATE, (sender, name) => {
301 console.log('handleIpcMessages: DEACTIVATE ' + name);
302 return _this.addonManager.deactivate(app, name);
305 checkInspectorCondition(appControl) {
306 let bundleDebug = (appControl.getData('__AUL_DEBUG__') === "1");
307 return (bundleDebug || this.inspectorEnabledByVconf);
309 launchInspector(appControl) {
310 var debugPort = wrt.startInspectorServer();
311 var data = { "port" : [ debugPort.toString() ] };
312 if (this.webApplication)
313 this.webApplication.debugPort = debugPort;
314 appControl.reply(data);
316 configureRuntime(appControl) {
317 this.configureRuntime = (param) => {}; // call once
319 // FIX ME : It must be supplemented to set a specific path
322 // AUL public key/Vconf - To support inspector
323 if (this.checkInspectorCondition(appControl)) {
324 this.launchInspector(appControl);
327 handleKeyEvents(key) {
331 console.log(key + ' is pressed');
340 console.log('No handler for the key ' + key);
345 _this.webApplication.keyEvent(key);
349 module.exports = Runtime;