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;
160 let originalUrl = _this.webApplication.mainWindow.getURL();
162 console.log(`appcontrol src = ${src}, original url = ${originalUrl}`);
163 if (src && originalUrl) {
164 let appcontrolUrl = new URL(src);
165 let oldUrl = new URL(originalUrl);
166 if ('file:' !== appcontrolUrl.protocol &&
167 (appcontrolUrl.protocol !== oldUrl.protocol ||
168 appcontrolUrl.host !== oldUrl.host ||
169 appcontrolUrl.pathname !== oldUrl.pathname)) {
171 } else if ('file:' === appcontrolUrl.protocol && (src !== originalUrl)) {
174 } else if (src !== originalUrl) {
177 } else if (src !== originalUrl) {
181 // handle http://tizen.org/appcontrol/operation/main operation specially.
182 // only menu-screen app can send launch request with main operation.
183 // in this case, web app should have to resume web app not reset.
184 if (reload && appControl.getOperation() == 'http://tizen.org/appcontrol/operation/main')
187 _this.webApplication.closeWindows();
188 _this.webApplication.mainWindow.loadURL(src);
190 _this.webApplication.sendAppControlEvent();
194 _this.setCookiePath();
195 _this.launchInspector(appControl);
197 wrt.on('suspend', function() {
198 console.log('suspend');
199 if (_this.webApplication)
200 _this.webApplication.suspend();
202 wrt.on('resume', function() {
203 console.log('resume');
204 if (_this.webApplication)
205 _this.webApplication.resume();
207 wrt.on('low-memory', function() {
208 console.log('low-memory');
209 if (_this.webApplication)
210 _this.webApplication.lowMemory();
212 wrt.on('message', function(event, type, params) {
213 console.log('message type(' + type + ') params : ' + params);
214 const app_id = params[0];
215 const vm = require('vm');
216 if (type === 'startService') {
217 if (_this.sandbox[app_id] === undefined) {
218 if (_this.sandbox_count === 0) {
219 new TizenExtension();
221 _this.sandbox_count++;
222 const fs = require('fs');
223 const Module = require('module');
224 _this.sandbox[app_id] = {
230 for(let key in global) {
231 _this.sandbox[app_id][key] = global[key];
233 _this.sandbox[app_id]['timer_manager'] = new TimerManager();
234 const timer_api = _this.sandbox[app_id]['timer_manager'].getTimerAPI();
235 for(let key in timer_api) {
236 _this.sandbox[app_id][key] = timer_api[key];
238 let standard_object_list = [ Error, EvalError, RangeError, ReferenceError,
239 SyntaxError, TypeError, URIError, Number, BigInt, Math, Date,
240 String, RegExp, Array, Int8Array, Uint8Array, Uint8ClampedArray,
241 Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array,
242 Float64Array, BigInt64Array, BigUint64Array, Map, Set, WeakMap,
243 WeakSet, ArrayBuffer, DataView, JSON, Promise, Reflect, Proxy,
244 Intl, Intl.Collator, Intl.DateTimeFormat, Intl.NumberFormat, Intl.PluralRules,
245 WebAssembly, WebAssembly.Module, WebAssembly.Instance, WebAssembly.Memory,
246 WebAssembly.Table, WebAssembly.CompileError, WebAssembly.LinkError,
247 WebAssembly.RuntimeError, Boolean, Function, Object, Symbol ];
248 for (let idx in standard_object_list) {
249 _this.sandbox[app_id][standard_object_list[idx].name] = standard_object_list[idx];
252 let code = fs.readFileSync(params[1]);
253 vm.runInNewContext(code, _this.sandbox[app_id], options);
255 if (_this.sandbox[app_id]['started'] === undefined) {
256 _this.sandbox[app_id]['started'] = true;
257 _this.sandbox[app_id]['stopped'] = undefined;
258 const start_callback_string = 'if (module.exports.onStart !== undefined) { module.exports.onStart(); }';
259 vm.runInContext(start_callback_string, _this.sandbox[app_id]);
261 console.log('UI service has been started.');
263 event.preventDefault();
264 } else if (type === 'stopService') {
265 if (_this.sandbox[app_id]['stopped'] === undefined) {
266 _this.sandbox_count--;
267 _this.sandbox[app_id]['stopped'] = true;
268 _this.sandbox[app_id]['started'] = undefined;
269 const stop_callback_string = 'if (module.exports.onStop !== undefined) { module.exports.onStop(); }';
270 vm.runInContext(stop_callback_string, _this.sandbox[app_id]);
271 _this.sandbox[app_id]['timer_manager'].releaseRemainingTimers();
272 for(let key in _this.sandbox[app_id]) {
273 delete _this.sandbox[app_id][key];
275 delete _this.sandbox[app_id];
276 if (_this.sandbox_count === 0) {
280 console.log('UI service has been stopped.');
282 event.preventDefault();
285 wrt.on('addon-installed', function(event, path) {
286 console.log('addon-installed at ' + path);
287 let dbInfo = AddonManager.checkAddon(path);
289 _this.addonPkgs.push(dbInfo);
292 wrt.on('addon-uninstalled', function(event, id) {
293 console.log('addon-unistalled named ' + id);
295 wrt.on('wgt-checking-done', function(event) {
296 console.log('wgt-checking-done');
297 AddonManager.updateDB(_this.addonPkgs);
299 /* FIXME: will uncheck after chromium-efl released */
300 if (wrt.getPlatformType() !== "product_tv") {
301 wrt.getInstalledPkg();
304 handleIpcMessages() {
306 ipcMain.on(IPC_MESSAGE.ADDONS.INSTALLED, (sender, name) => {
307 console.log('handleIpcMessages: INSTALLED ' + name);
308 _this.addonManager.build();
309 return _this.addonManager.activate(app, name);
311 ipcMain.on(IPC_MESSAGE.ADDONS.UNINSTALLED, (sender, name, pkg) => {
312 console.log('handleIpcMessages: UNINSTALLED ' + name);
313 _this.addonManager.deactivate(app, name);
314 /* FIXME: will uncheck after chromium-efl released */
315 if (wrt.getPlatformType() !== "product_tv") {
316 wrt.reqUninstallPkg(pkg);
320 ipcMain.on(IPC_MESSAGE.ADDONS.ACTIVATE, (sender, name) => {
321 console.log('handleIpcMessages: ACTIVATE ' + name);
322 return _this.addonManager.activate(app, name);
324 ipcMain.on(IPC_MESSAGE.ADDONS.DEACTIVATE, (sender, name) => {
325 console.log('handleIpcMessages: DEACTIVATE ' + name);
326 return _this.addonManager.deactivate(app, name);
329 checkInspectorCondition(appControl) {
330 let bundleDebug = (appControl.getData('__AUL_DEBUG__') === "1");
331 return (bundleDebug || this.inspectorEnabledByVconf);
334 this.setCookiePath = () => {}; // call once
335 console.log('setCookiePath');
337 // FIX ME : It must be supplemented to set a specific path
340 launchInspector(appControl) {
341 this.launchInspector = (param) => {}; // call once
342 console.log('launchInspector');
344 // AUL public key/Vconf - To support inspector
345 if (this.checkInspectorCondition(appControl)) {
346 var debugPort = wrt.startInspectorServer();
347 var data = { "port" : [ debugPort.toString() ] };
348 if (this.webApplication)
349 this.webApplication.debugPort = debugPort;
350 appControl.reply(data);
353 handleKeyEvents(key) {
357 console.log(key + ' is pressed');
366 console.log('No handler for the key ' + key);
371 _this.webApplication.keyEvent(key);
375 module.exports = Runtime;