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 WAS_EVENT = require('./was_event');
25 const WebApplication = require('./web_application');
28 constructor(options) {
29 this.webApplication = null;
30 this.handleIpcMessages();
31 this.addonManager = null;
32 this.isLaunched = false;
33 this.inspectorEnabledByVconf = false;
35 this.webContents = null;
37 this.isTVProfile = (wrt.getPlatformType() === 'product_tv');
40 app.on('before-quit', function(event) {
41 console.log('before-quit');
42 if (!wrt.isElectronApp()) {
43 _this.webApplication.quit();
44 _this.webApplication.finalize();
45 _this.webApplication = null;
48 app.on('will-quit', function(event) {
49 console.log('will-quit');
50 _this.addonManager.deactivateAll(app);
52 app.on('quit', function(event) {
56 app.on('browser-window-blur', function() {
57 console.log('browser-window-blur');
59 app.on('browser-window-focus', function() {
60 console.log('browser-window-focus');
62 app.on('browser-window-created', function() {
63 console.log('browser-window-created');
64 if (!_this.isLaunched) {
65 _this.addonManager.activateAll(app);
66 _this.isLaunched = true;
69 app.on('gpu-process-crashed', function() {
70 console.error('gpu-process-crashed');
72 app.on('window-all-closed', function(event) {
73 console.log('window-all-closed');
76 app.on('will-finish-launching', function(event) {
77 console.log('will-finish-launching');
79 app.on('web-contents-created', function(event, webContents) {
80 console.log('web-contents-created');
81 _this.webContents = webContents;
82 _this.webContents.on('before-input-event', function(event, input) {
83 if (_this.isLaunched && _this.webApplication) {
84 _this.handleKeyEvents(input.key);
88 app.once('ready', function(event) {
90 _this.addonManager = new AddonManager();
91 if (!options.noAddons) {
92 _this.addonManager.build();
94 wrt.importCertificate('');
96 wrt.on('app-control', function(event, appControl) {
97 console.log('app-control');
98 let loadInfo = appControl.getLoadInfo();
99 let src = loadInfo.getSrc();
101 if (wrt.isElectronApp()) {
102 console.log('Electron App launch');
103 const Module = require('module');
104 Module.globalPaths.push(wrt.getAppPath());
105 let filePath = src[7] === '/' ? src.substr(8) : src.substr(7); // strip "file://"
106 let pkgJson = require(filePath);
107 let pos = filePath.lastIndexOf('/');
109 let mainJsPath = (pos !== -1 ? filePath.substr(0, pos + 1) : '') +
110 (pkgJson.main || 'index.js');
111 console.log('loading path:', mainJsPath);
112 Module._load(mainJsPath, Module, true);
115 console.log('Tizen Web App launch');
116 let launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode');
117 if (!_this.webApplication) {
118 console.log('Creating WebApplication');
119 options.isAddonAvailable = !options.noAddons &&
120 _this.addonManager.isAddonAvailable();
121 options.launchMode = launchMode;
122 _this.webApplication = new WebApplication(options);
123 _this.webApplication.addonEmitter =
124 _this.addonManager.evt_emitter_;
125 _this.inspectorEnabledByVconf = wrt.needUseInspector();
126 if (_this.inspectorEnabledByVconf && launchMode != 'backgroundExecution') {
127 _this.webApplication.inspectorSrc = src;
130 _this.webApplication.mainWindow.loadURL(src);
131 _this.webApplication.prelaunch(src);
133 console.log('Handling app-control event');
134 if (_this.webApplication.preloadStatus == 'readyToShow') {
135 _this.webApplication.show();
137 if (launchMode != 'backgroundAtStartup') {
138 _this.webApplication.preloadStatus = 'none';
142 let skipReload = appControl.getData('SkipReload');
143 if (skipReload == 'Yes') {
144 console.log('skipping reload');
145 // TODO : Need to care this situation and decide to pass the addon event emitter to resume()
146 _this.webApplication.resume();
150 let reload = loadInfo.getReload() || _this.webApplication.isAlwaysReload;
152 if (_this.isTVProfile) {
153 console.log(`src = ${src}, app-control uri = ${_this.webApplication.mainWindow.getURL()}`);
154 const url = require('url');
155 let appcontrolUrl = url.parse(src);
156 let originUrl = url.parse(_this.webApplication.mainWindow.getURL());
157 if (appcontrolUrl.protocol !== originUrl.protocol ||
158 appcontrolUrl.host !== originUrl.host ||
159 appcontrolUrl.pathname !== originUrl.pathname) {
163 if (src != _this.webApplication.mainWindow.getURL()) {
168 // handle http://tizen.org/appcontrol/operation/main operation specially.
169 // only menu-screen app can send launch request with main operation.
170 // in this case, web app should have to resume web app not reset.
171 if (reload && appControl.getOperation() == 'http://tizen.org/appcontrol/operation/main')
174 _this.webApplication.closeWindows();
175 _this.webApplication.mainWindow.loadURL(src);
177 _this.webApplication.sendAppControlEvent();
181 _this.configureRuntime(appControl);
183 wrt.on('suspend', function() {
184 console.log('suspend');
185 if (_this.webApplication)
186 _this.webApplication.suspend();
188 wrt.on('resume', function() {
189 console.log('resume');
190 if (_this.webApplication)
191 _this.webApplication.resume();
193 wrt.on('low-memory', function() {
194 console.log('low-memory');
195 if (_this.webApplication)
196 _this.webApplication.lowMemory();
198 wrt.on('message', function(event, type, params) {
199 console.log('message type(' + type + ') params : ' + params);
200 const app_id = params[0];
201 const vm = require('vm');
202 if (type === 'startService') {
203 if (_this.sandbox[app_id] === undefined) {
204 const fs = require('fs');
205 const Module = require('module');
206 _this.sandbox[app_id] = {
211 for(let key in global) {
212 _this.sandbox[app_id][key] = global[key];
214 _this.sandbox[app_id]['timer_manager'] = new TimerManager();
215 const timer_api = _this.sandbox[app_id]['timer_manager'].getTimerAPI();
216 for(let key in timer_api) {
217 _this.sandbox[app_id][key] = timer_api[key];
220 let code = fs.readFileSync(params[1]);
221 vm.runInNewContext(code, _this.sandbox[app_id], options);
223 if (_this.sandbox[app_id]['started'] === undefined) {
224 _this.sandbox[app_id]['started'] = true;
225 _this.sandbox[app_id]['stopped'] = undefined;
226 const start_callback_string = 'if (module.exports.onStart !== undefined) { module.exports.onStart(); }';
227 vm.runInContext(start_callback_string, _this.sandbox[app_id]);
229 console.log('UI service has been started.');
231 event.preventDefault();
232 } else if (type === 'stopService') {
233 if (_this.sandbox[app_id]['stopped'] === undefined) {
234 _this.sandbox[app_id]['stopped'] = true;
235 _this.sandbox[app_id]['started'] = undefined;
236 const stop_callback_string = 'if (module.exports.onStop !== undefined) { module.exports.onStop(); }';
237 vm.runInContext(stop_callback_string, _this.sandbox[app_id]);
238 _this.sandbox[app_id]['timer_manager'].releaseRemainingTimers();
239 _this.sandbox[app_id] = undefined;
241 console.log('UI service has been stopped.');
243 event.preventDefault();
246 wrt.on('addon-installed', function(event, path) {
247 console.log('addon-installed at ' + path);
248 let dbInfo = AddonManager.checkAddon(path);
250 _this.addonPkgs.push(dbInfo);
253 wrt.on('addon-uninstalled', function(event, id) {
254 console.log('addon-unistalled named ' + id);
256 wrt.on('wgt-checking-done', function(event) {
257 console.log('wgt-checking-done');
258 AddonManager.updateDB(_this.addonPkgs);
260 /* FIXME: will uncheck after chromium-efl released */
261 if (wrt.getPlatformType() !== "product_tv") {
262 wrt.getInstalledPkg();
265 handleIpcMessages() {
267 ipcMain.on(IPC_MESSAGE.ADDONS.INSTALLED, (sender, name) => {
268 console.log('handleIpcMessages: INSTALLED ' + name);
269 _this.addonManager.build();
270 return _this.addonManager.activate(app, name);
272 ipcMain.on(IPC_MESSAGE.ADDONS.UNINSTALLED, (sender, name, pkg) => {
273 console.log('handleIpcMessages: UNINSTALLED ' + name);
274 _this.addonManager.deactivate(app, name);
275 /* FIXME: will uncheck after chromium-efl released */
276 if (wrt.getPlatformType() !== "product_tv") {
277 wrt.reqUninstallPkg(pkg);
281 ipcMain.on(IPC_MESSAGE.ADDONS.ACTIVATE, (sender, name) => {
282 console.log('handleIpcMessages: ACTIVATE ' + name);
283 return _this.addonManager.activate(app, name);
285 ipcMain.on(IPC_MESSAGE.ADDONS.DEACTIVATE, (sender, name) => {
286 console.log('handleIpcMessages: DEACTIVATE ' + name);
287 return _this.addonManager.deactivate(app, name);
290 checkInspectorCondition(appControl) {
291 let bundleDebug = (appControl.getData('__AUL_DEBUG__') === "1");
292 return (bundleDebug || this.inspectorEnabledByVconf);
294 launchInspector(appControl) {
295 var debugPort = wrt.startInspectorServer();
296 var data = { "port" : [ debugPort.toString() ] };
297 if (this.webApplication)
298 this.webApplication.debugPort = debugPort;
299 appControl.reply(data);
301 configureRuntime(appControl) {
302 this.configureRuntime = (param) => {}; // call once
304 // FIX ME : It must be supplemented to set a specific path
307 // AUL public key/Vconf - To support inspector
308 if (this.checkInspectorCondition(appControl)) {
309 this.launchInspector(appControl);
312 handleKeyEvents(key) {
316 console.log(key + ' is pressed');
325 console.log('No handler for the key ' + key);
330 _this.webApplication.keyEvent(key);
334 module.exports = Runtime;