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.sandbox_count = 0;
37 this.webContents = null;
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');
83 _this.setCookiePath();
84 _this.webContents = webContents;
85 _this.webContents.on('before-input-event', function(event, input) {
86 if (_this.isLaunched && _this.webApplication) {
87 _this.handleKeyEvents(input.key);
91 app.once('ready', function(event) {
93 _this.addonManager = new AddonManager();
94 if (!options.noAddons) {
95 _this.addonManager.build();
98 wrt.tv.importCertificate('');
99 wrt.tv.optimizeCache();
102 wrt.on('app-control', function(event, appControl) {
103 console.log('app-control');
104 let loadInfo = appControl.getLoadInfo();
105 let src = loadInfo.getSrc();
107 if (wrt.isElectronApp()) {
108 console.log('Electron App launch');
109 const Module = require('module');
110 Module.globalPaths.push(wrt.getAppPath());
111 let filePath = src[7] === '/' ? src.substr(8) : src.substr(7); // strip "file://"
112 let pkgJson = require(filePath);
113 let pos = filePath.lastIndexOf('/');
115 let mainJsPath = (pos !== -1 ? filePath.substr(0, pos + 1) : '') +
116 (pkgJson.main || 'index.js');
117 console.log('loading path:', mainJsPath);
118 Module._load(mainJsPath, Module, true);
121 console.log('Tizen Web App launch');
122 let launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode');
123 if (!_this.webApplication) {
124 console.log('Creating WebApplication');
125 options.isAddonAvailable = !options.noAddons &&
126 _this.addonManager.isAddonAvailable();
127 options.launchMode = launchMode;
128 _this.webApplication = new WebApplication(options);
129 _this.webApplication.addonEmitter =
130 _this.addonManager.evt_emitter_;
132 _this.inspectorEnabledByVconf = wrt.tv.needUseInspector();
133 if (_this.inspectorEnabledByVconf && launchMode != 'backgroundExecution') {
134 _this.webApplication.inspectorSrc = src;
138 _this.webApplication.mainWindow.loadURL(src);
139 _this.webApplication.prelaunch(src);
141 console.log('Handling app-control event');
142 if (_this.webApplication.preloadStatus == 'readyToShow') {
143 _this.webApplication.show();
145 if (launchMode != 'backgroundAtStartup') {
146 _this.webApplication.preloadStatus = 'none';
150 let skipReload = appControl.getData('SkipReload');
151 if (skipReload == 'Yes') {
152 console.log('skipping reload');
153 // TODO : Need to care this situation and decide to pass the addon event emitter to resume()
154 _this.webApplication.resume();
158 let reload = loadInfo.getReload() || _this.webApplication.isAlwaysReload;
161 console.log(`src = ${src}, app-control uri = ${_this.webApplication.mainWindow.getURL()}`);
162 const url = require('url');
163 let appcontrolUrl = url.parse(src);
164 let originUrl = url.parse(_this.webApplication.mainWindow.getURL());
165 if (appcontrolUrl.protocol !== originUrl.protocol ||
166 appcontrolUrl.host !== originUrl.host ||
167 appcontrolUrl.pathname !== originUrl.pathname) {
171 if (src != _this.webApplication.mainWindow.getURL()) {
176 // handle http://tizen.org/appcontrol/operation/main operation specially.
177 // only menu-screen app can send launch request with main operation.
178 // in this case, web app should have to resume web app not reset.
179 if (reload && appControl.getOperation() == 'http://tizen.org/appcontrol/operation/main')
182 _this.webApplication.closeWindows();
183 _this.webApplication.mainWindow.loadURL(src);
185 _this.webApplication.sendAppControlEvent();
189 _this.setCookiePath();
190 _this.launchInspector(appControl);
192 wrt.on('suspend', function() {
193 console.log('suspend');
194 if (_this.webApplication)
195 _this.webApplication.suspend();
197 wrt.on('resume', function() {
198 console.log('resume');
199 if (_this.webApplication)
200 _this.webApplication.resume();
202 wrt.on('low-memory', function() {
203 console.log('low-memory');
204 if (_this.webApplication)
205 _this.webApplication.lowMemory();
207 wrt.on('message', function(event, type, params) {
208 console.log('message type(' + type + ') params : ' + params);
209 const app_id = params[0];
210 const vm = require('vm');
211 if (type === 'startService') {
212 if (_this.sandbox[app_id] === undefined) {
213 if (_this.sandbox_count === 0) {
214 new TizenExtension();
216 _this.sandbox_count++;
217 const fs = require('fs');
218 const Module = require('module');
219 _this.sandbox[app_id] = {
225 for(let key in global) {
226 _this.sandbox[app_id][key] = global[key];
228 _this.sandbox[app_id]['timer_manager'] = new TimerManager();
229 const timer_api = _this.sandbox[app_id]['timer_manager'].getTimerAPI();
230 for(let key in timer_api) {
231 _this.sandbox[app_id][key] = timer_api[key];
233 let standard_object_list = [ Error, EvalError, RangeError, ReferenceError,
234 SyntaxError, TypeError, URIError, Number, BigInt, Math, Date,
235 String, RegExp, Array, Int8Array, Uint8Array, Uint8ClampedArray,
236 Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array,
237 Float64Array, BigInt64Array, BigUint64Array, Map, Set, WeakMap,
238 WeakSet, ArrayBuffer, DataView, JSON, Promise, Reflect, Proxy,
239 Intl, Intl.Collator, Intl.DateTimeFormat, Intl.NumberFormat, Intl.PluralRules,
240 WebAssembly, WebAssembly.Module, WebAssembly.Instance, WebAssembly.Memory,
241 WebAssembly.Table, WebAssembly.CompileError, WebAssembly.LinkError,
242 WebAssembly.RuntimeError, Boolean, Function, Object, Symbol ];
243 for (let idx in standard_object_list) {
244 _this.sandbox[app_id][standard_object_list[idx].name] = standard_object_list[idx];
247 let code = fs.readFileSync(params[1]);
248 vm.runInNewContext(code, _this.sandbox[app_id], options);
250 if (_this.sandbox[app_id]['started'] === undefined) {
251 _this.sandbox[app_id]['started'] = true;
252 _this.sandbox[app_id]['stopped'] = undefined;
253 const start_callback_string = 'if (module.exports.onStart !== undefined) { module.exports.onStart(); }';
254 vm.runInContext(start_callback_string, _this.sandbox[app_id]);
256 console.log('UI service has been started.');
258 event.preventDefault();
259 } else if (type === 'stopService') {
260 if (_this.sandbox[app_id]['stopped'] === undefined) {
261 _this.sandbox_count--;
262 _this.sandbox[app_id]['stopped'] = true;
263 _this.sandbox[app_id]['started'] = undefined;
264 const stop_callback_string = 'if (module.exports.onStop !== undefined) { module.exports.onStop(); }';
265 vm.runInContext(stop_callback_string, _this.sandbox[app_id]);
266 _this.sandbox[app_id]['timer_manager'].releaseRemainingTimers();
267 for(let key in _this.sandbox[app_id]) {
268 delete _this.sandbox[app_id][key];
270 delete _this.sandbox[app_id];
271 if (_this.sandbox_count === 0) {
275 console.log('UI service has been stopped.');
277 event.preventDefault();
280 wrt.on('addon-installed', function(event, path) {
281 console.log('addon-installed at ' + path);
282 let dbInfo = AddonManager.checkAddon(path);
284 _this.addonPkgs.push(dbInfo);
287 wrt.on('addon-uninstalled', function(event, id) {
288 console.log('addon-unistalled named ' + id);
290 wrt.on('wgt-checking-done', function(event) {
291 console.log('wgt-checking-done');
292 AddonManager.updateDB(_this.addonPkgs);
294 /* FIXME: will uncheck after chromium-efl released */
295 if (wrt.getPlatformType() !== "product_tv") {
296 wrt.getInstalledPkg();
299 handleIpcMessages() {
301 ipcMain.on(IPC_MESSAGE.ADDONS.INSTALLED, (sender, name) => {
302 console.log('handleIpcMessages: INSTALLED ' + name);
303 _this.addonManager.build();
304 return _this.addonManager.activate(app, name);
306 ipcMain.on(IPC_MESSAGE.ADDONS.UNINSTALLED, (sender, name, pkg) => {
307 console.log('handleIpcMessages: UNINSTALLED ' + name);
308 _this.addonManager.deactivate(app, name);
309 /* FIXME: will uncheck after chromium-efl released */
310 if (wrt.getPlatformType() !== "product_tv") {
311 wrt.reqUninstallPkg(pkg);
315 ipcMain.on(IPC_MESSAGE.ADDONS.ACTIVATE, (sender, name) => {
316 console.log('handleIpcMessages: ACTIVATE ' + name);
317 return _this.addonManager.activate(app, name);
319 ipcMain.on(IPC_MESSAGE.ADDONS.DEACTIVATE, (sender, name) => {
320 console.log('handleIpcMessages: DEACTIVATE ' + name);
321 return _this.addonManager.deactivate(app, name);
324 checkInspectorCondition(appControl) {
325 let bundleDebug = (appControl.getData('__AUL_DEBUG__') === "1");
326 return (bundleDebug || this.inspectorEnabledByVconf);
329 this.setCookiePath = () => {}; // call once
330 console.log('setCookiePath');
332 // FIX ME : It must be supplemented to set a specific path
335 launchInspector(appControl) {
336 this.launchInspector = (param) => {}; // call once
337 console.log('launchInspector');
339 // AUL public key/Vconf - To support inspector
340 if (this.checkInspectorCondition(appControl)) {
341 var debugPort = wrt.startInspectorServer();
342 var data = { "port" : [ debugPort.toString() ] };
343 if (this.webApplication)
344 this.webApplication.debugPort = debugPort;
345 appControl.reply(data);
348 handleKeyEvents(key) {
352 console.log(key + ' is pressed');
361 console.log('No handler for the key ' + key);
366 _this.webApplication.keyEvent(key);
370 module.exports = Runtime;